Merge remote-tracking branch 'upstream/master' into pg8000
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
[submodule "gluon/packages/dal"]
|
||||
path = gluon/packages/dal
|
||||
url = https://github.com/web2py/pydal.git
|
||||
+5
-43
@@ -4,59 +4,21 @@ python:
|
||||
- '2.6'
|
||||
- '2.7'
|
||||
- 'pypy'
|
||||
|
||||
install:
|
||||
- pip install -e .
|
||||
env:
|
||||
- DB=sqlite:memory
|
||||
- DB=mysql://root:@localhost/test_w2p
|
||||
- DB=postgres://postgres:@localhost/test_w2p
|
||||
- DB=google:datastore
|
||||
# - DB=google:datastore+ndb
|
||||
- DB=mongodb://mongodb:mongodb@localhost/test_w2p
|
||||
- DB=imap://imap:imap@localhost:993
|
||||
|
||||
before_script:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install unittest2; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install coverage; fi;
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install python-coveralls; fi
|
||||
- if [[ $DB == postgres* ]]; then pip install psycopg2; fi;
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install pysqlite; fi
|
||||
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
|
||||
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
|
||||
- if [[ $DB == postgres* ]]; then psql -c 'create extension postgis;' -U postgres -d test_w2p; fi
|
||||
|
||||
|
||||
# Install last sdk for app engine (update only whenever a new release is available)
|
||||
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/files/google_appengine_1.8.9.zip -nv; fi
|
||||
- if [[ $DB == google* ]]; then unzip -q google_appengine_1.8.9.zip; fi
|
||||
- if [[ $DB == google* ]]; then mv -f ./google_appengine/google ./google; fi
|
||||
|
||||
- if [[ $DB == mongodb* ]]; then pip install pymongo; fi
|
||||
- if [[ $DB == mongodb* ]]; then mongo test_w2p --eval 'db.addUser("mongodb", "mongodb");'; fi
|
||||
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
|
||||
#Temporal solution to travis issue #155
|
||||
- sudo chmod 777 /dev/shm
|
||||
- sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
|
||||
matrix:
|
||||
exclude:
|
||||
- python: 'pypy'
|
||||
env: DB=postgres://postgres:@localhost/test_w2p
|
||||
- python: 'pypy'
|
||||
env: DB=mysql://root:@localhost/test_w2p
|
||||
- python: 'pypy'
|
||||
env: DB=google:datastore
|
||||
- python: '2.6'
|
||||
env: DB=google:datastore
|
||||
# - python: '2.6'
|
||||
# env: DB=google:datastore+ndb
|
||||
|
||||
|
||||
script: export COVERAGE_PROCESS_START=gluon/tests/coverage.ini; ./web2py.py --run_system_tests --with_coverage
|
||||
|
||||
after_success:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coverage combine; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --config_file=gluon/tests/coverage.ini; fi
|
||||
|
||||
|
||||
notifications:
|
||||
email: true
|
||||
|
||||
services: mongodb
|
||||
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
## 2.9.12
|
||||
|
||||
- Tornado HTTPS support, thanks Diego
|
||||
- Modular DAL, thanks Giovanni
|
||||
- Added coverage support, thanks Niphlod
|
||||
- More tests, thanks Niphlod and Paolo Valleri
|
||||
- Added support for show_if in readonly sqlform, thanks Paolo
|
||||
- Improved scheduler, thanks Niphlod
|
||||
- Email timeout support
|
||||
- Made web2py's custom_import work with circular imports, thanks Jack Kuan
|
||||
- Added Portuguese, Catalan, and Burmese translations
|
||||
- Allow map_hyphen to work for application names, thanks Tim Nyborg
|
||||
- New module appconfig.py, thanks Niphlod
|
||||
- Added geospatial support to Teradata adaptor, thanks Andrew Willimott
|
||||
- Many bug fixes
|
||||
|
||||
|
||||
|
||||
## 2.9.6 - 2.9.10
|
||||
|
||||
- fixed support of GAE + SQL
|
||||
|
||||
@@ -37,7 +37,7 @@ update:
|
||||
echo "remember that pymysql was tweaked"
|
||||
src:
|
||||
### Use semantic versioning
|
||||
echo 'Version 2.10.0-beta+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
|
||||
echo 'Version 2.9.12-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
|
||||
### rm -f all junk files
|
||||
make clean
|
||||
### clean up baisc apps
|
||||
@@ -61,7 +61,7 @@ src:
|
||||
### build web2py_src.zip
|
||||
echo '' > NEWINSTALL
|
||||
mv web2py_src.zip web2py_src_old.zip | echo 'no old'
|
||||
cd ..; zip -r web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/gluon/*.py web2py/gluon/contrib/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in 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/web2py.py web2py/anyserver.py web2py/gluon/*.py web2py/gluon/dal/* web2py/gluon/contrib/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in 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:
|
||||
make src
|
||||
|
||||
@@ -1 +1 @@
|
||||
Version 2.10.0-beta+timestamp.2014.10.16.15.58.50
|
||||
Version 2.9.12-stable+timestamp.2015.01.17.00.07.04
|
||||
|
||||
@@ -589,7 +589,7 @@ def edit():
|
||||
if 'settings' in request.vars:
|
||||
if request.post_vars: #save new preferences
|
||||
post_vars = request.post_vars.items()
|
||||
# Since unchecked checkbox are not serialized, we must set them as false by hand to store the correct preference in the settings
|
||||
# Since unchecked checkbox are not serialized, we must set them as false by hand to store the correct preference in the settings
|
||||
post_vars+= [(opt, 'false') for opt in preferences if opt not in request.post_vars ]
|
||||
if config.save(post_vars):
|
||||
response.headers["web2py-component-flash"] = T('Preferences saved correctly')
|
||||
@@ -775,12 +775,12 @@ def edit():
|
||||
view_link=view_link,
|
||||
editviewlinks=editviewlinks,
|
||||
id=IS_SLUG()(filename)[0],
|
||||
force= True if (request.vars.restore or
|
||||
force= True if (request.vars.restore or
|
||||
request.vars.revert) else False)
|
||||
plain_html = response.render('default/edit_js.html', file_details)
|
||||
file_details['plain_html'] = plain_html
|
||||
if is_mobile:
|
||||
return response.render('default.mobile/edit.html',
|
||||
return response.render('default.mobile/edit.html',
|
||||
file_details, editor_settings=preferences)
|
||||
else:
|
||||
return response.json(file_details)
|
||||
@@ -1278,7 +1278,7 @@ def create_file():
|
||||
path = abspath(request.vars.location)
|
||||
else:
|
||||
if request.vars.dir:
|
||||
request.vars.location += request.vars.dir + '/'
|
||||
request.vars.location += request.vars.dir + '/'
|
||||
app = get_app(name=request.vars.location.split('/')[0])
|
||||
path = apath(request.vars.location, r=request)
|
||||
filename = re.sub('[^\w./-]+', '_', request.vars.filename)
|
||||
@@ -1387,7 +1387,7 @@ def create_file():
|
||||
from gluon import *\n""")[1:]
|
||||
|
||||
elif (path[-8:] == '/static/') or (path[-9:] == '/private/'):
|
||||
if (request.vars.plugin and
|
||||
if (request.vars.plugin and
|
||||
not filename.startswith('plugin_%s/' % request.vars.plugin)):
|
||||
filename = 'plugin_%s/%s' % (request.vars.plugin, filename)
|
||||
text = ''
|
||||
@@ -1434,37 +1434,37 @@ def create_file():
|
||||
""" % URL('edit', args=[app,request.vars.dir,filename])
|
||||
return ''
|
||||
else:
|
||||
redirect(request.vars.sender + anchor)
|
||||
redirect(request.vars.sender + anchor)
|
||||
|
||||
|
||||
def listfiles(app, dir, regexp='.*\.py$'):
|
||||
files = sorted(
|
||||
files = sorted(
|
||||
listdir(apath('%(app)s/%(dir)s/' % {'app':app, 'dir':dir}, r=request), regexp))
|
||||
files = [x.replace('\\', '/') for x in files if not x.endswith('.bak')]
|
||||
return files
|
||||
|
||||
files = [x.replace('\\', '/') for x in files if not x.endswith('.bak')]
|
||||
return files
|
||||
|
||||
def editfile(path,file,vars={}, app = None):
|
||||
args=(path,file) if 'app' in vars else (app,path,file)
|
||||
url = URL('edit', args=args, vars=vars)
|
||||
return A(file, _class='editor_filelink', _href=url, _style='word-wrap: nowrap;')
|
||||
|
||||
args=(path,file) if 'app' in vars else (app,path,file)
|
||||
url = URL('edit', args=args, vars=vars)
|
||||
return A(file, _class='editor_filelink', _href=url, _style='word-wrap: nowrap;')
|
||||
|
||||
def files_menu():
|
||||
app = request.vars.app or 'welcome'
|
||||
dirs=[{'name':'models', 'reg':'.*\.py$'},
|
||||
app = request.vars.app or 'welcome'
|
||||
dirs=[{'name':'models', 'reg':'.*\.py$'},
|
||||
{'name':'controllers', 'reg':'.*\.py$'},
|
||||
{'name':'views', 'reg':'[\w/\-]+(\.\w+)+$'},
|
||||
{'name':'modules', 'reg':'.*\.py$'},
|
||||
{'name':'static', 'reg': '[^\.#].*'},
|
||||
{'name':'private', 'reg':'.*\.py$'}]
|
||||
result_files = []
|
||||
for dir in dirs:
|
||||
result_files.append(TAG[''](LI(dir['name'], _class="nav-header component", _onclick="collapse('" + dir['name'] + "_files');"),
|
||||
LI(UL(*[LI(editfile(dir['name'], f, dict(id=dir['name'] + f.replace('.','__')), app), _style="overflow:hidden", _id=dir['name']+"__"+f.replace('.','__'))
|
||||
for f in listfiles(app, dir['name'], regexp=dir['reg'])],
|
||||
_class="nav nav-list small-font"),
|
||||
_id=dir['name'] + '_files', _style="display: none;")))
|
||||
return dict(result_files = result_files)
|
||||
|
||||
result_files = []
|
||||
for dir in dirs:
|
||||
result_files.append(TAG[''](LI(dir['name'], _class="nav-header component", _onclick="collapse('" + dir['name'] + "_files');"),
|
||||
LI(UL(*[LI(editfile(dir['name'], f, dict(id=dir['name'] + f.replace('.','__')), app), _style="overflow:hidden", _id=dir['name']+"__"+f.replace('.','__'))
|
||||
for f in listfiles(app, dir['name'], regexp=dir['reg'])],
|
||||
_class="nav nav-list small-font"),
|
||||
_id=dir['name'] + '_files', _style="display: none;")))
|
||||
return dict(result_files = result_files)
|
||||
|
||||
def upload_file():
|
||||
""" File uploading handler """
|
||||
if request.vars and not request.vars.token == session.token:
|
||||
@@ -1941,4 +1941,3 @@ def install_plugin():
|
||||
T('unable to install plugin "%s"', filename)
|
||||
redirect(URL(f="plugins", args=[app,]))
|
||||
return dict(form=form, app=app, plugin=plugin, source=source)
|
||||
|
||||
|
||||
+480
-480
@@ -1,480 +1,480 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'!langcode!': 'cs-cz',
|
||||
'!langname!': 'čeština',
|
||||
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': 'Kolonka "Upravit" je nepovinný výraz, například "pole1=\'nováhodnota\'". Výsledky databázového JOINu nemůžete mazat ani upravovat.',
|
||||
'"User Exception" debug mode. An error ticket could be issued!': '"User Exception" debug mode. An error ticket could be issued!',
|
||||
'%%{Row} in Table': '%%{řádek} v tabulce',
|
||||
'%%{Row} selected': 'označených %%{řádek}',
|
||||
'%s %%{row} deleted': '%s smazaných %%{záznam}',
|
||||
'%s %%{row} updated': '%s upravených %%{záznam}',
|
||||
'%s selected': '%s označených',
|
||||
'%Y-%m-%d': '%d.%m.%Y',
|
||||
'%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S',
|
||||
'(requires internet access)': '(vyžaduje připojení k internetu)',
|
||||
'(requires internet access, experimental)': '(requires internet access, experimental)',
|
||||
'(something like "it-it")': '(například "cs-cs")',
|
||||
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)',
|
||||
'@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}',
|
||||
'About': 'O programu',
|
||||
'About application': 'O aplikaci',
|
||||
'Access Control': 'Řízení přístupu',
|
||||
'Add breakpoint': 'Přidat bod přerušení',
|
||||
'Additional code for your application': 'Další kód pro Vaši aplikaci',
|
||||
'Admin design page': 'Admin design page',
|
||||
'Admin language': 'jazyk rozhraní',
|
||||
'Administrative interface': 'pro administrátorské rozhraní klikněte sem',
|
||||
'Administrative Interface': 'Administrátorské rozhraní',
|
||||
'administrative interface': 'rozhraní pro správu',
|
||||
'Administrator Password:': 'Administrátorské heslo:',
|
||||
'Ajax Recipes': 'Recepty s ajaxem',
|
||||
'An error occured, please %s the page': 'An error occured, please %s the page',
|
||||
'and rename it:': 'a přejmenovat na:',
|
||||
'appadmin': 'appadmin',
|
||||
'appadmin is disabled because insecure channel': 'appadmin je zakázaná bez zabezpečeného spojení',
|
||||
'Application': 'Application',
|
||||
'application "%s" uninstalled': 'application "%s" odinstalována',
|
||||
'application compiled': 'aplikace zkompilována',
|
||||
'Application name:': 'Název aplikace:',
|
||||
'are not used': 'nepoužita',
|
||||
'are not used yet': 'ještě nepoužita',
|
||||
'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?',
|
||||
'Are you sure you want to uninstall application "%s"?': 'Opravdu chcete odinstalovat aplikaci "%s"?',
|
||||
'arguments': 'arguments',
|
||||
'at char %s': 'at char %s',
|
||||
'at line %s': 'at line %s',
|
||||
'ATTENTION:': 'ATTENTION:',
|
||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.',
|
||||
'Available Databases and Tables': 'Dostupné databáze a tabulky',
|
||||
'back': 'zpět',
|
||||
'Back to wizard': 'Back to wizard',
|
||||
'Basics': 'Basics',
|
||||
'Begin': 'Začít',
|
||||
'breakpoint': 'bod přerušení',
|
||||
'Breakpoints': 'Body přerušení',
|
||||
'breakpoints': 'body přerušení',
|
||||
'Buy this book': 'Koupit web2py knihu',
|
||||
'Cache': 'Cache',
|
||||
'cache': 'cache',
|
||||
'Cache Keys': 'Klíče cache',
|
||||
'cache, errors and sessions cleaned': 'cache, chyby a relace byly pročištěny',
|
||||
'can be a git repo': 'může to být git repo',
|
||||
'Cancel': 'Storno',
|
||||
'Cannot be empty': 'Nemůže být prázdné',
|
||||
'Change Admin Password': 'Změnit heslo pro správu',
|
||||
'Change admin password': 'Změnit heslo pro správu aplikací',
|
||||
'Change password': 'Změna hesla',
|
||||
'check all': 'vše označit',
|
||||
'Check for upgrades': 'Zkusit aktualizovat',
|
||||
'Check to delete': 'Označit ke smazání',
|
||||
'Check to delete:': 'Označit ke smazání:',
|
||||
'Checking for upgrades...': 'Zjišťuji, zda jsou k dispozici aktualizace...',
|
||||
'Clean': 'Pročistit',
|
||||
'Clear CACHE?': 'Vymazat CACHE?',
|
||||
'Clear DISK': 'Vymazat DISK',
|
||||
'Clear RAM': 'Vymazat RAM',
|
||||
'Click row to expand traceback': 'Pro rozbalení stopy, klikněte na řádek',
|
||||
'Click row to view a ticket': 'Pro zobrazení chyby (ticketu), klikněte na řádku...',
|
||||
'Client IP': 'IP adresa klienta',
|
||||
'code': 'code',
|
||||
'Code listing': 'Code listing',
|
||||
'collapse/expand all': 'vše sbalit/rozbalit',
|
||||
'Community': 'Komunita',
|
||||
'Compile': 'Zkompilovat',
|
||||
'compiled application removed': 'zkompilovaná aplikace smazána',
|
||||
'Components and Plugins': 'Komponenty a zásuvné moduly',
|
||||
'Condition': 'Podmínka',
|
||||
'continue': 'continue',
|
||||
'Controller': 'Kontrolér (Controller)',
|
||||
'Controllers': 'Kontroléry',
|
||||
'controllers': 'kontroléry',
|
||||
'Copyright': 'Copyright',
|
||||
'Count': 'Počet',
|
||||
'Create': 'Vytvořit',
|
||||
'create file with filename:': 'vytvořit soubor s názvem:',
|
||||
'created by': 'vytvořil',
|
||||
'Created By': 'Vytvořeno - kým',
|
||||
'Created On': 'Vytvořeno - kdy',
|
||||
'crontab': 'crontab',
|
||||
'Current request': 'Aktuální požadavek',
|
||||
'Current response': 'Aktuální odpověď',
|
||||
'Current session': 'Aktuální relace',
|
||||
'currently running': 'právě běží',
|
||||
'currently saved or': 'uloženo nebo',
|
||||
'customize me!': 'upravte mě!',
|
||||
'data uploaded': 'data nahrána',
|
||||
'Database': 'Rozhraní databáze',
|
||||
'Database %s select': 'databáze %s výběr',
|
||||
'Database administration': 'Database administration',
|
||||
'database administration': 'správa databáze',
|
||||
'Date and Time': 'Datum a čas',
|
||||
'day': 'den',
|
||||
'db': 'db',
|
||||
'DB Model': 'Databázový model',
|
||||
'Debug': 'Ladění',
|
||||
'defines tables': 'defines tables',
|
||||
'Delete': 'Smazat',
|
||||
'delete': 'smazat',
|
||||
'delete all checked': 'smazat vše označené',
|
||||
'delete plugin': 'delete plugin',
|
||||
'Delete this file (you will be asked to confirm deletion)': 'Smazat tento soubor (budete požádán o potvrzení mazání)',
|
||||
'Delete:': 'Smazat:',
|
||||
'deleted after first hit': 'smazat po prvním dosažení',
|
||||
'Demo': 'Demo',
|
||||
'Deploy': 'Nahrát',
|
||||
'Deploy on Google App Engine': 'Nahrát na Google App Engine',
|
||||
'Deploy to OpenShift': 'Nahrát na OpenShift',
|
||||
'Deployment Recipes': 'Postupy pro deployment',
|
||||
'Description': 'Popis',
|
||||
'design': 'návrh',
|
||||
'Detailed traceback description': 'Podrobný výpis prostředí',
|
||||
'details': 'podrobnosti',
|
||||
'direction: ltr': 'směr: ltr',
|
||||
'Disable': 'Zablokovat',
|
||||
'DISK': 'DISK',
|
||||
'Disk Cache Keys': 'Klíče diskové cache',
|
||||
'Disk Cleared': 'Disk smazán',
|
||||
'docs': 'dokumentace',
|
||||
'Documentation': 'Dokumentace',
|
||||
"Don't know what to do?": 'Nevíte kudy kam?',
|
||||
'done!': 'hotovo!',
|
||||
'Download': 'Stáhnout',
|
||||
'download layouts': 'stáhnout moduly rozvržení stránky',
|
||||
'download plugins': 'stáhnout zásuvné moduly',
|
||||
'E-mail': 'E-mail',
|
||||
'Edit': 'Upravit',
|
||||
'edit all': 'edit all',
|
||||
'Edit application': 'Správa aplikace',
|
||||
'edit controller': 'edit controller',
|
||||
'Edit current record': 'Upravit aktuální záznam',
|
||||
'Edit Profile': 'Upravit profil',
|
||||
'edit views:': 'upravit pohled:',
|
||||
'Editing file "%s"': 'Úprava souboru "%s"',
|
||||
'Editing Language file': 'Úprava jazykového souboru',
|
||||
'Editing Plural Forms File': 'Editing Plural Forms File',
|
||||
'Email and SMS': 'Email a SMS',
|
||||
'Enable': 'Odblokovat',
|
||||
'enter a number between %(min)g and %(max)g': 'zadejte číslo mezi %(min)g a %(max)g',
|
||||
'enter an integer between %(min)g and %(max)g': 'zadejte celé číslo mezi %(min)g a %(max)g',
|
||||
'Error': 'Chyba',
|
||||
'Error logs for "%(app)s"': 'Seznam výskytu chyb pro aplikaci "%(app)s"',
|
||||
'Error snapshot': 'Snapshot chyby',
|
||||
'Error ticket': 'Ticket chyby',
|
||||
'Errors': 'Chyby',
|
||||
'Exception %(extype)s: %(exvalue)s': 'Exception %(extype)s: %(exvalue)s',
|
||||
'Exception %s': 'Exception %s',
|
||||
'Exception instance attributes': 'Prvky instance výjimky',
|
||||
'Expand Abbreviation': 'Expand Abbreviation',
|
||||
'export as csv file': 'exportovat do .csv souboru',
|
||||
'exposes': 'vystavuje',
|
||||
'exposes:': 'vystavuje funkce:',
|
||||
'extends': 'rozšiřuje',
|
||||
'failed to compile file because:': 'soubor se nepodařilo zkompilovat, protože:',
|
||||
'FAQ': 'Často kladené dotazy',
|
||||
'File': 'Soubor',
|
||||
'file': 'soubor',
|
||||
'file "%(filename)s" created': 'file "%(filename)s" created',
|
||||
'file saved on %(time)s': 'soubor uložen %(time)s',
|
||||
'file saved on %s': 'soubor uložen %s',
|
||||
'Filename': 'Název souboru',
|
||||
'filter': 'filtr',
|
||||
'Find Next': 'Najít další',
|
||||
'Find Previous': 'Najít předchozí',
|
||||
'First name': 'Křestní jméno',
|
||||
'Forgot username?': 'Zapomněl jste svoje přihlašovací jméno?',
|
||||
'forgot username?': 'zapomněl jste svoje přihlašovací jméno?',
|
||||
'Forms and Validators': 'Formuláře a validátory',
|
||||
'Frames': 'Frames',
|
||||
'Free Applications': 'Aplikace zdarma',
|
||||
'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.',
|
||||
'Generate': 'Vytvořit',
|
||||
'Get from URL:': 'Stáhnout z internetu:',
|
||||
'Git Pull': 'Git Pull',
|
||||
'Git Push': 'Git Push',
|
||||
'Globals##debug': 'Globální proměnné',
|
||||
'go!': 'OK!',
|
||||
'Goto': 'Goto',
|
||||
'graph model': 'graph model',
|
||||
'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena',
|
||||
'Group ID': 'ID skupiny',
|
||||
'Groups': 'Skupiny',
|
||||
'Hello World': 'Ahoj světe',
|
||||
'Help': 'Nápověda',
|
||||
'Hide/Show Translated strings': 'Skrýt/Zobrazit přeložené texty',
|
||||
'Hits': 'Kolikrát dosaženo',
|
||||
'Home': 'Domovská stránka',
|
||||
'honored only if the expression evaluates to true': 'brát v potaz jen když se tato podmínka vyhodnotí kladně',
|
||||
'How did you get here?': 'Jak jste se sem vlastně dostal?',
|
||||
'If start the upgrade, be patient, it may take a while to download': 'If start the upgrade, be patient, it may take a while to download',
|
||||
'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': 'import',
|
||||
'Import/Export': 'Import/Export',
|
||||
'includes': 'zahrnuje',
|
||||
'Index': 'Index',
|
||||
'insert new': 'vložit nový záznam ',
|
||||
'insert new %s': 'vložit nový záznam %s',
|
||||
'inspect attributes': 'inspect attributes',
|
||||
'Install': 'Instalovat',
|
||||
'Installed applications': 'Nainstalované aplikace',
|
||||
'Interaction at %s line %s': 'Interakce v %s, na řádce %s',
|
||||
'Interactive console': 'Interaktivní příkazová řádka',
|
||||
'Internal State': 'Vnitřní stav',
|
||||
'Introduction': 'Úvod',
|
||||
'Invalid email': 'Neplatný email',
|
||||
'Invalid password': 'Nesprávné heslo',
|
||||
'invalid password.': 'neplatné heslo',
|
||||
'Invalid Query': 'Neplatný dotaz',
|
||||
'invalid request': 'Neplatný požadavek',
|
||||
'Is Active': 'Je aktivní',
|
||||
'It is %s %%{day} today.': 'Dnes je to %s %%{den}.',
|
||||
'Key': 'Klíč',
|
||||
'Key bindings': 'Vazby klíčů',
|
||||
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
|
||||
'languages': 'jazyky',
|
||||
'Languages': 'Jazyky',
|
||||
'Last name': 'Příjmení',
|
||||
'Last saved on:': 'Naposledy uloženo:',
|
||||
'Layout': 'Rozvržení stránky (layout)',
|
||||
'Layout Plugins': 'Moduly rozvržení stránky (Layout Plugins)',
|
||||
'Layouts': 'Rozvržení stránek',
|
||||
'License for': 'Licence pro',
|
||||
'Line number': 'Číslo řádku',
|
||||
'LineNo': 'Č.řádku',
|
||||
'Live Chat': 'Online pokec',
|
||||
'loading...': 'nahrávám...',
|
||||
'locals': 'locals',
|
||||
'Locals##debug': 'Lokální proměnné',
|
||||
'Logged in': 'Přihlášení proběhlo úspěšně',
|
||||
'Logged out': 'Odhlášení proběhlo úspěšně',
|
||||
'Login': 'Přihlásit se',
|
||||
'login': 'přihlásit se',
|
||||
'Login to the Administrative Interface': 'Přihlásit se do Správce aplikací',
|
||||
'logout': 'odhlásit se',
|
||||
'Logout': 'Odhlásit se',
|
||||
'Lost Password': 'Zapomněl jste heslo',
|
||||
'Lost password?': 'Zapomněl jste heslo?',
|
||||
'lost password?': 'zapomněl jste heslo?',
|
||||
'Manage': 'Manage',
|
||||
'Manage Cache': 'Manage Cache',
|
||||
'Menu Model': 'Model rozbalovací nabídky',
|
||||
'Models': 'Modely',
|
||||
'models': 'modely',
|
||||
'Modified By': 'Změněno - kým',
|
||||
'Modified On': 'Změněno - kdy',
|
||||
'Modules': 'Moduly',
|
||||
'modules': 'moduly',
|
||||
'My Sites': 'Správa aplikací',
|
||||
'Name': 'Jméno',
|
||||
'new application "%s" created': 'nová aplikace "%s" vytvořena',
|
||||
'New Application Wizard': 'Nový průvodce aplikací',
|
||||
'New application wizard': 'Nový průvodce aplikací',
|
||||
'New password': 'Nové heslo',
|
||||
'New Record': 'Nový záznam',
|
||||
'new record inserted': 'nový záznam byl založen',
|
||||
'New simple application': 'Vytvořit primitivní aplikaci',
|
||||
'next': 'next',
|
||||
'next 100 rows': 'dalších 100 řádků',
|
||||
'No databases in this application': 'V této aplikaci nejsou žádné databáze',
|
||||
'No Interaction yet': 'Ještě žádná interakce nenastala',
|
||||
'No ticket_storage.txt found under /private folder': 'Soubor ticket_storage.txt v adresáři /private nenalezen',
|
||||
'Object or table name': 'Objekt či tabulka',
|
||||
'Old password': 'Původní heslo',
|
||||
'online designer': 'online návrhář',
|
||||
'Online examples': 'Příklady online',
|
||||
'Open new app in new window': 'Open new app in new window',
|
||||
'or alternatively': 'or alternatively',
|
||||
'Or Get from URL:': 'Or Get from URL:',
|
||||
'or import from csv file': 'nebo importovat z .csv souboru',
|
||||
'Origin': 'Původ',
|
||||
'Original/Translation': 'Originál/Překlad',
|
||||
'Other Plugins': 'Ostatní moduly',
|
||||
'Other Recipes': 'Ostatní zásuvné moduly',
|
||||
'Overview': 'Přehled',
|
||||
'Overwrite installed app': 'Přepsat instalovanou aplikaci',
|
||||
'Pack all': 'Zabalit',
|
||||
'Pack compiled': 'Zabalit zkompilované',
|
||||
'pack plugin': 'pack plugin',
|
||||
'password': 'heslo',
|
||||
'Password': 'Heslo',
|
||||
"Password fields don't match": 'Hesla se neshodují',
|
||||
'Peeking at file': 'Peeking at file',
|
||||
'Please': 'Prosím',
|
||||
'Plugin "%s" in application': 'Plugin "%s" in application',
|
||||
'plugins': 'zásuvné moduly',
|
||||
'Plugins': 'Zásuvné moduly',
|
||||
'Plural Form #%s': 'Plural Form #%s',
|
||||
'Plural-Forms:': 'Množná čísla:',
|
||||
'Powered by': 'Poháněno',
|
||||
'Preface': 'Předmluva',
|
||||
'previous 100 rows': 'předchozích 100 řádků',
|
||||
'Private files': 'Soukromé soubory',
|
||||
'private files': 'soukromé soubory',
|
||||
'profile': 'profil',
|
||||
'Project Progress': 'Vývoj projektu',
|
||||
'Python': 'Python',
|
||||
'Query:': 'Dotaz:',
|
||||
'Quick Examples': 'Krátké příklady',
|
||||
'RAM': 'RAM',
|
||||
'RAM Cache Keys': 'Klíče RAM Cache',
|
||||
'Ram Cleared': 'RAM smazána',
|
||||
'Readme': 'Nápověda',
|
||||
'Recipes': 'Postupy jak na to',
|
||||
'Record': 'Záznam',
|
||||
'record does not exist': 'záznam neexistuje',
|
||||
'Record ID': 'ID záznamu',
|
||||
'Record id': 'id záznamu',
|
||||
'refresh': 'obnovte',
|
||||
'register': 'registrovat',
|
||||
'Register': 'Zaregistrovat se',
|
||||
'Registration identifier': 'Registrační identifikátor',
|
||||
'Registration key': 'Registrační klíč',
|
||||
'reload': 'reload',
|
||||
'Reload routes': 'Znovu nahrát cesty',
|
||||
'Remember me (for 30 days)': 'Zapamatovat na 30 dní',
|
||||
'Remove compiled': 'Odstranit zkompilované',
|
||||
'Removed Breakpoint on %s at line %s': 'Bod přerušení smazán - soubor %s na řádce %s',
|
||||
'Replace': 'Zaměnit',
|
||||
'Replace All': 'Zaměnit vše',
|
||||
'request': 'request',
|
||||
'Reset Password key': 'Reset registračního klíče',
|
||||
'response': 'response',
|
||||
'restart': 'restart',
|
||||
'restore': 'obnovit',
|
||||
'Retrieve username': 'Získat přihlašovací jméno',
|
||||
'return': 'return',
|
||||
'revert': 'vrátit se k původnímu',
|
||||
'Role': 'Role',
|
||||
'Rows in Table': 'Záznamy v tabulce',
|
||||
'Rows selected': 'Záznamů zobrazeno',
|
||||
'rules are not defined': 'pravidla nejsou definována',
|
||||
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Spustí testy v tomto souboru (ke spuštění všech testů, použijte tlačítko 'test')",
|
||||
'Running on %s': 'Běží na %s',
|
||||
'Save': 'Uložit',
|
||||
'Save file:': 'Save file:',
|
||||
'Save via Ajax': 'Uložit pomocí Ajaxu',
|
||||
'Saved file hash:': 'hash uloženého souboru:',
|
||||
'Semantic': 'Modul semantic',
|
||||
'Services': 'Služby',
|
||||
'session': 'session',
|
||||
'session expired': 'session expired',
|
||||
'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s',
|
||||
'shell': 'příkazová řádka',
|
||||
'Singular Form': 'Singular Form',
|
||||
'Site': 'Správa aplikací',
|
||||
'Size of cache:': 'Velikost cache:',
|
||||
'skip to generate': 'skip to generate',
|
||||
'Sorry, could not find mercurial installed': 'Bohužel mercurial není nainstalován.',
|
||||
'Start a new app': 'Vytvořit novou aplikaci',
|
||||
'Start searching': 'Začít hledání',
|
||||
'Start wizard': 'Spustit průvodce',
|
||||
'state': 'stav',
|
||||
'Static': 'Static',
|
||||
'static': 'statické soubory',
|
||||
'Static files': 'Statické soubory',
|
||||
'Statistics': 'Statistika',
|
||||
'Step': 'Step',
|
||||
'step': 'step',
|
||||
'stop': 'stop',
|
||||
'Stylesheet': 'CSS styly',
|
||||
'submit': 'odeslat',
|
||||
'Submit': 'Odeslat',
|
||||
'successful': 'úspěšně',
|
||||
'Support': 'Podpora',
|
||||
'Sure you want to delete this object?': 'Opravdu chcete smazat tento objekt?',
|
||||
'Table': 'tabulka',
|
||||
'Table name': 'Název tabulky',
|
||||
'Temporary': 'Dočasný',
|
||||
'test': 'test',
|
||||
'Testing application': 'Testing application',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Dotaz" je podmínka, například "db.tabulka1.pole1==\'hodnota\'". Podmínka "db.tabulka1.pole1==db.tabulka2.pole2" pak vytvoří SQL JOIN.',
|
||||
'The application logic, each URL path is mapped in one exposed function in the controller': 'Logika aplikace: každá URL je mapována na funkci vystavovanou kontrolérem.',
|
||||
'The Core': 'Jádro (The Core)',
|
||||
'The data representation, define database tables and sets': 'Reprezentace dat: definovat tabulky databáze a záznamy',
|
||||
'The output of the file is a dictionary that was rendered by the view %s': 'Výstup ze souboru je slovník, který se zobrazil v pohledu %s.',
|
||||
'The presentations layer, views are also known as templates': 'Prezentační vrstva: pohledy či templaty (šablony)',
|
||||
'The Views': 'Pohledy (The Views)',
|
||||
'There are no controllers': 'There are no controllers',
|
||||
'There are no modules': 'There are no modules',
|
||||
'There are no plugins': 'Žádné moduly nejsou instalovány.',
|
||||
'There are no private files': 'Žádné soukromé soubory neexistují.',
|
||||
'There are no static files': 'There are no static files',
|
||||
'There are no translators, only default language is supported': 'There are no translators, only default language is supported',
|
||||
'There are no views': 'There are no views',
|
||||
'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.',
|
||||
'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.',
|
||||
'This App': 'Tato aplikace',
|
||||
'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.',
|
||||
'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk',
|
||||
'This is the %(filename)s template': 'This is the %(filename)s template',
|
||||
'this page to see if a breakpoint was hit and debug interaction is required.': 'tuto stránku, abyste uviděli, zda se dosáhlo bodu přerušení.',
|
||||
'Ticket': 'Ticket',
|
||||
'Ticket ID': 'Ticket ID',
|
||||
'Time in Cache (h:m:s)': 'Čas v Cache (h:m:s)',
|
||||
'Timestamp': 'Časové razítko',
|
||||
'to previous version.': 'k předchozí verzi.',
|
||||
'To create a plugin, name a file/folder plugin_[name]': 'Zásuvný modul vytvoříte tak, že pojmenujete soubor/adresář plugin_[jméno modulu]',
|
||||
'To emulate a breakpoint programatically, write:': 'K nastavení bodu přerušení v kódu programu, napište:',
|
||||
'to use the debugger!': ', abyste mohli ladící program používat!',
|
||||
'toggle breakpoint': 'vyp./zap. bod přerušení',
|
||||
'Toggle Fullscreen': 'Na celou obrazovku a zpět',
|
||||
'too short': 'Příliš krátké',
|
||||
'Traceback': 'Traceback',
|
||||
'Translation strings for the application': 'Překlad textů pro aplikaci',
|
||||
'try something like': 'try something like',
|
||||
'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení',
|
||||
'try view': 'try view',
|
||||
'Twitter': 'Twitter',
|
||||
'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.',
|
||||
'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.',
|
||||
'Unable to check for upgrades': 'Unable to check for upgrades',
|
||||
'unable to parse csv file': 'csv soubor nedá sa zpracovat',
|
||||
'uncheck all': 'vše odznačit',
|
||||
'Uninstall': 'Odinstalovat',
|
||||
'update': 'aktualizovat',
|
||||
'update all languages': 'aktualizovat všechny jazyky',
|
||||
'Update:': 'Upravit:',
|
||||
'Upgrade': 'Upgrade',
|
||||
'upgrade now': 'upgrade now',
|
||||
'upgrade now to %s': 'upgrade now to %s',
|
||||
'upload': 'nahrát',
|
||||
'Upload': 'Upload',
|
||||
'Upload a package:': 'Nahrát balík:',
|
||||
'Upload and install packed application': 'Nahrát a instalovat zabalenou aplikaci',
|
||||
'upload file:': 'nahrát soubor:',
|
||||
'upload plugin file:': 'nahrát soubor modulu:',
|
||||
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použijte (...)&(...) pro AND, (...)|(...) pro OR a ~(...) pro NOT pro sestavení složitějších dotazů.',
|
||||
'User %(id)s Logged-in': 'Uživatel %(id)s přihlášen',
|
||||
'User %(id)s Logged-out': 'Uživatel %(id)s odhlášen',
|
||||
'User %(id)s Password changed': 'Uživatel %(id)s změnil heslo',
|
||||
'User %(id)s Profile updated': 'Uživatel %(id)s upravil profil',
|
||||
'User %(id)s Registered': 'Uživatel %(id)s se zaregistroval',
|
||||
'User %(id)s Username retrieved': 'Uživatel %(id)s si nachal zaslat přihlašovací jméno',
|
||||
'User ID': 'ID uživatele',
|
||||
'Username': 'Přihlašovací jméno',
|
||||
'variables': 'variables',
|
||||
'Verify Password': 'Zopakujte heslo',
|
||||
'Version': 'Verze',
|
||||
'Version %s.%s.%s (%s) %s': 'Verze %s.%s.%s (%s) %s',
|
||||
'Versioning': 'Verzování',
|
||||
'Videos': 'Videa',
|
||||
'View': 'Pohled (View)',
|
||||
'Views': 'Pohledy',
|
||||
'views': 'pohledy',
|
||||
'Web Framework': 'Web Framework',
|
||||
'web2py is up to date': 'Máte aktuální verzi web2py.',
|
||||
'web2py online debugger': 'Ladící online web2py program',
|
||||
'web2py Recent Tweets': 'Štěbetání na Twitteru o web2py',
|
||||
'web2py upgrade': 'web2py upgrade',
|
||||
'web2py upgraded; please restart it': 'web2py upgraded; please restart it',
|
||||
'Welcome': 'Vítejte',
|
||||
'Welcome to web2py': 'Vitejte ve web2py',
|
||||
'Welcome to web2py!': 'Vítejte ve web2py!',
|
||||
'Which called the function %s located in the file %s': 'která zavolala funkci %s v souboru (kontroléru) %s.',
|
||||
'You are successfully running web2py': 'Úspěšně jste spustili web2py.',
|
||||
'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Nastavovat a mazat body přerušení je též možno v rámci editování zdrojového souboru přes tlačítko Vyp./Zap. bod přerušení',
|
||||
'You can modify this application and adapt it to your needs': 'Tuto aplikaci si můžete upravit a přizpůsobit ji svým potřebám.',
|
||||
'You need to set up and reach a': 'Je třeba nejprve nastavit a dojít až na',
|
||||
'You visited the url %s': 'Navštívili jste stránku %s,',
|
||||
'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Aplikace bude blokována než se klikne na jedno z tlačítek (další, krok, pokračovat, atd.)',
|
||||
'You can inspect variables using the console bellow': 'Níže pomocí příkazové řádky si můžete prohlédnout proměnné',
|
||||
}
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'!langcode!': 'cs-cz',
|
||||
'!langname!': 'čeština',
|
||||
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': 'Kolonka "Upravit" je nepovinný výraz, například "pole1=\'nováhodnota\'". Výsledky databázového JOINu nemůžete mazat ani upravovat.',
|
||||
'"User Exception" debug mode. An error ticket could be issued!': '"User Exception" debug mode. An error ticket could be issued!',
|
||||
'%%{Row} in Table': '%%{řádek} v tabulce',
|
||||
'%%{Row} selected': 'označených %%{řádek}',
|
||||
'%s %%{row} deleted': '%s smazaných %%{záznam}',
|
||||
'%s %%{row} updated': '%s upravených %%{záznam}',
|
||||
'%s selected': '%s označených',
|
||||
'%Y-%m-%d': '%d.%m.%Y',
|
||||
'%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S',
|
||||
'(requires internet access)': '(vyžaduje připojení k internetu)',
|
||||
'(requires internet access, experimental)': '(requires internet access, experimental)',
|
||||
'(something like "it-it")': '(například "cs-cs")',
|
||||
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)',
|
||||
'@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}',
|
||||
'About': 'O programu',
|
||||
'About application': 'O aplikaci',
|
||||
'Access Control': 'Řízení přístupu',
|
||||
'Add breakpoint': 'Přidat bod přerušení',
|
||||
'Additional code for your application': 'Další kód pro Vaši aplikaci',
|
||||
'Admin design page': 'Admin design page',
|
||||
'Admin language': 'jazyk rozhraní',
|
||||
'Administrative interface': 'pro administrátorské rozhraní klikněte sem',
|
||||
'Administrative Interface': 'Administrátorské rozhraní',
|
||||
'administrative interface': 'rozhraní pro správu',
|
||||
'Administrator Password:': 'Administrátorské heslo:',
|
||||
'Ajax Recipes': 'Recepty s ajaxem',
|
||||
'An error occured, please %s the page': 'An error occured, please %s the page',
|
||||
'and rename it:': 'a přejmenovat na:',
|
||||
'appadmin': 'appadmin',
|
||||
'appadmin is disabled because insecure channel': 'appadmin je zakázaná bez zabezpečeného spojení',
|
||||
'Application': 'Application',
|
||||
'application "%s" uninstalled': 'application "%s" odinstalována',
|
||||
'application compiled': 'aplikace zkompilována',
|
||||
'Application name:': 'Název aplikace:',
|
||||
'are not used': 'nepoužita',
|
||||
'are not used yet': 'ještě nepoužita',
|
||||
'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?',
|
||||
'Are you sure you want to uninstall application "%s"?': 'Opravdu chcete odinstalovat aplikaci "%s"?',
|
||||
'arguments': 'arguments',
|
||||
'at char %s': 'at char %s',
|
||||
'at line %s': 'at line %s',
|
||||
'ATTENTION:': 'ATTENTION:',
|
||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.',
|
||||
'Available Databases and Tables': 'Dostupné databáze a tabulky',
|
||||
'back': 'zpět',
|
||||
'Back to wizard': 'Back to wizard',
|
||||
'Basics': 'Basics',
|
||||
'Begin': 'Začít',
|
||||
'breakpoint': 'bod přerušení',
|
||||
'Breakpoints': 'Body přerušení',
|
||||
'breakpoints': 'body přerušení',
|
||||
'Buy this book': 'Koupit web2py knihu',
|
||||
'Cache': 'Cache',
|
||||
'cache': 'cache',
|
||||
'Cache Keys': 'Klíče cache',
|
||||
'cache, errors and sessions cleaned': 'cache, chyby a relace byly pročištěny',
|
||||
'can be a git repo': 'může to být git repo',
|
||||
'Cancel': 'Storno',
|
||||
'Cannot be empty': 'Nemůže být prázdné',
|
||||
'Change Admin Password': 'Změnit heslo pro správu',
|
||||
'Change admin password': 'Změnit heslo pro správu aplikací',
|
||||
'Change password': 'Změna hesla',
|
||||
'check all': 'vše označit',
|
||||
'Check for upgrades': 'Zkusit aktualizovat',
|
||||
'Check to delete': 'Označit ke smazání',
|
||||
'Check to delete:': 'Označit ke smazání:',
|
||||
'Checking for upgrades...': 'Zjišťuji, zda jsou k dispozici aktualizace...',
|
||||
'Clean': 'Pročistit',
|
||||
'Clear CACHE?': 'Vymazat CACHE?',
|
||||
'Clear DISK': 'Vymazat DISK',
|
||||
'Clear RAM': 'Vymazat RAM',
|
||||
'Click row to expand traceback': 'Pro rozbalení stopy, klikněte na řádek',
|
||||
'Click row to view a ticket': 'Pro zobrazení chyby (ticketu), klikněte na řádku...',
|
||||
'Client IP': 'IP adresa klienta',
|
||||
'code': 'code',
|
||||
'Code listing': 'Code listing',
|
||||
'collapse/expand all': 'vše sbalit/rozbalit',
|
||||
'Community': 'Komunita',
|
||||
'Compile': 'Zkompilovat',
|
||||
'compiled application removed': 'zkompilovaná aplikace smazána',
|
||||
'Components and Plugins': 'Komponenty a zásuvné moduly',
|
||||
'Condition': 'Podmínka',
|
||||
'continue': 'continue',
|
||||
'Controller': 'Kontrolér (Controller)',
|
||||
'Controllers': 'Kontroléry',
|
||||
'controllers': 'kontroléry',
|
||||
'Copyright': 'Copyright',
|
||||
'Count': 'Počet',
|
||||
'Create': 'Vytvořit',
|
||||
'create file with filename:': 'vytvořit soubor s názvem:',
|
||||
'created by': 'vytvořil',
|
||||
'Created By': 'Vytvořeno - kým',
|
||||
'Created On': 'Vytvořeno - kdy',
|
||||
'crontab': 'crontab',
|
||||
'Current request': 'Aktuální požadavek',
|
||||
'Current response': 'Aktuální odpověď',
|
||||
'Current session': 'Aktuální relace',
|
||||
'currently running': 'právě běží',
|
||||
'currently saved or': 'uloženo nebo',
|
||||
'customize me!': 'upravte mě!',
|
||||
'data uploaded': 'data nahrána',
|
||||
'Database': 'Rozhraní databáze',
|
||||
'Database %s select': 'databáze %s výběr',
|
||||
'Database administration': 'Database administration',
|
||||
'database administration': 'správa databáze',
|
||||
'Date and Time': 'Datum a čas',
|
||||
'day': 'den',
|
||||
'db': 'db',
|
||||
'DB Model': 'Databázový model',
|
||||
'Debug': 'Ladění',
|
||||
'defines tables': 'defines tables',
|
||||
'Delete': 'Smazat',
|
||||
'delete': 'smazat',
|
||||
'delete all checked': 'smazat vše označené',
|
||||
'delete plugin': 'delete plugin',
|
||||
'Delete this file (you will be asked to confirm deletion)': 'Smazat tento soubor (budete požádán o potvrzení mazání)',
|
||||
'Delete:': 'Smazat:',
|
||||
'deleted after first hit': 'smazat po prvním dosažení',
|
||||
'Demo': 'Demo',
|
||||
'Deploy': 'Nahrát',
|
||||
'Deploy on Google App Engine': 'Nahrát na Google App Engine',
|
||||
'Deploy to OpenShift': 'Nahrát na OpenShift',
|
||||
'Deployment Recipes': 'Postupy pro deployment',
|
||||
'Description': 'Popis',
|
||||
'design': 'návrh',
|
||||
'Detailed traceback description': 'Podrobný výpis prostředí',
|
||||
'details': 'podrobnosti',
|
||||
'direction: ltr': 'směr: ltr',
|
||||
'Disable': 'Zablokovat',
|
||||
'DISK': 'DISK',
|
||||
'Disk Cache Keys': 'Klíče diskové cache',
|
||||
'Disk Cleared': 'Disk smazán',
|
||||
'docs': 'dokumentace',
|
||||
'Documentation': 'Dokumentace',
|
||||
"Don't know what to do?": 'Nevíte kudy kam?',
|
||||
'done!': 'hotovo!',
|
||||
'Download': 'Stáhnout',
|
||||
'download layouts': 'stáhnout moduly rozvržení stránky',
|
||||
'download plugins': 'stáhnout zásuvné moduly',
|
||||
'E-mail': 'E-mail',
|
||||
'Edit': 'Upravit',
|
||||
'edit all': 'edit all',
|
||||
'Edit application': 'Správa aplikace',
|
||||
'edit controller': 'edit controller',
|
||||
'Edit current record': 'Upravit aktuální záznam',
|
||||
'Edit Profile': 'Upravit profil',
|
||||
'edit views:': 'upravit pohled:',
|
||||
'Editing file "%s"': 'Úprava souboru "%s"',
|
||||
'Editing Language file': 'Úprava jazykového souboru',
|
||||
'Editing Plural Forms File': 'Editing Plural Forms File',
|
||||
'Email and SMS': 'Email a SMS',
|
||||
'Enable': 'Odblokovat',
|
||||
'enter a number between %(min)g and %(max)g': 'zadejte číslo mezi %(min)g a %(max)g',
|
||||
'enter an integer between %(min)g and %(max)g': 'zadejte celé číslo mezi %(min)g a %(max)g',
|
||||
'Error': 'Chyba',
|
||||
'Error logs for "%(app)s"': 'Seznam výskytu chyb pro aplikaci "%(app)s"',
|
||||
'Error snapshot': 'Snapshot chyby',
|
||||
'Error ticket': 'Ticket chyby',
|
||||
'Errors': 'Chyby',
|
||||
'Exception %(extype)s: %(exvalue)s': 'Exception %(extype)s: %(exvalue)s',
|
||||
'Exception %s': 'Exception %s',
|
||||
'Exception instance attributes': 'Prvky instance výjimky',
|
||||
'Expand Abbreviation': 'Expand Abbreviation',
|
||||
'export as csv file': 'exportovat do .csv souboru',
|
||||
'exposes': 'vystavuje',
|
||||
'exposes:': 'vystavuje funkce:',
|
||||
'extends': 'rozšiřuje',
|
||||
'failed to compile file because:': 'soubor se nepodařilo zkompilovat, protože:',
|
||||
'FAQ': 'Často kladené dotazy',
|
||||
'File': 'Soubor',
|
||||
'file': 'soubor',
|
||||
'file "%(filename)s" created': 'file "%(filename)s" created',
|
||||
'file saved on %(time)s': 'soubor uložen %(time)s',
|
||||
'file saved on %s': 'soubor uložen %s',
|
||||
'Filename': 'Název souboru',
|
||||
'filter': 'filtr',
|
||||
'Find Next': 'Najít další',
|
||||
'Find Previous': 'Najít předchozí',
|
||||
'First name': 'Křestní jméno',
|
||||
'Forgot username?': 'Zapomněl jste svoje přihlašovací jméno?',
|
||||
'forgot username?': 'zapomněl jste svoje přihlašovací jméno?',
|
||||
'Forms and Validators': 'Formuláře a validátory',
|
||||
'Frames': 'Frames',
|
||||
'Free Applications': 'Aplikace zdarma',
|
||||
'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.',
|
||||
'Generate': 'Vytvořit',
|
||||
'Get from URL:': 'Stáhnout z internetu:',
|
||||
'Git Pull': 'Git Pull',
|
||||
'Git Push': 'Git Push',
|
||||
'Globals##debug': 'Globální proměnné',
|
||||
'go!': 'OK!',
|
||||
'Goto': 'Goto',
|
||||
'graph model': 'graph model',
|
||||
'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena',
|
||||
'Group ID': 'ID skupiny',
|
||||
'Groups': 'Skupiny',
|
||||
'Hello World': 'Ahoj světe',
|
||||
'Help': 'Nápověda',
|
||||
'Hide/Show Translated strings': 'Skrýt/Zobrazit přeložené texty',
|
||||
'Hits': 'Kolikrát dosaženo',
|
||||
'Home': 'Domovská stránka',
|
||||
'honored only if the expression evaluates to true': 'brát v potaz jen když se tato podmínka vyhodnotí kladně',
|
||||
'How did you get here?': 'Jak jste se sem vlastně dostal?',
|
||||
'If start the upgrade, be patient, it may take a while to download': 'If start the upgrade, be patient, it may take a while to download',
|
||||
'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': 'import',
|
||||
'Import/Export': 'Import/Export',
|
||||
'includes': 'zahrnuje',
|
||||
'Index': 'Index',
|
||||
'insert new': 'vložit nový záznam ',
|
||||
'insert new %s': 'vložit nový záznam %s',
|
||||
'inspect attributes': 'inspect attributes',
|
||||
'Install': 'Instalovat',
|
||||
'Installed applications': 'Nainstalované aplikace',
|
||||
'Interaction at %s line %s': 'Interakce v %s, na řádce %s',
|
||||
'Interactive console': 'Interaktivní příkazová řádka',
|
||||
'Internal State': 'Vnitřní stav',
|
||||
'Introduction': 'Úvod',
|
||||
'Invalid email': 'Neplatný email',
|
||||
'Invalid password': 'Nesprávné heslo',
|
||||
'invalid password.': 'neplatné heslo',
|
||||
'Invalid Query': 'Neplatný dotaz',
|
||||
'invalid request': 'Neplatný požadavek',
|
||||
'Is Active': 'Je aktivní',
|
||||
'It is %s %%{day} today.': 'Dnes je to %s %%{den}.',
|
||||
'Key': 'Klíč',
|
||||
'Key bindings': 'Vazby klíčů',
|
||||
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
|
||||
'languages': 'jazyky',
|
||||
'Languages': 'Jazyky',
|
||||
'Last name': 'Příjmení',
|
||||
'Last saved on:': 'Naposledy uloženo:',
|
||||
'Layout': 'Rozvržení stránky (layout)',
|
||||
'Layout Plugins': 'Moduly rozvržení stránky (Layout Plugins)',
|
||||
'Layouts': 'Rozvržení stránek',
|
||||
'License for': 'Licence pro',
|
||||
'Line number': 'Číslo řádku',
|
||||
'LineNo': 'Č.řádku',
|
||||
'Live Chat': 'Online pokec',
|
||||
'loading...': 'nahrávám...',
|
||||
'locals': 'locals',
|
||||
'Locals##debug': 'Lokální proměnné',
|
||||
'Logged in': 'Přihlášení proběhlo úspěšně',
|
||||
'Logged out': 'Odhlášení proběhlo úspěšně',
|
||||
'Login': 'Přihlásit se',
|
||||
'login': 'přihlásit se',
|
||||
'Login to the Administrative Interface': 'Přihlásit se do Správce aplikací',
|
||||
'logout': 'odhlásit se',
|
||||
'Logout': 'Odhlásit se',
|
||||
'Lost Password': 'Zapomněl jste heslo',
|
||||
'Lost password?': 'Zapomněl jste heslo?',
|
||||
'lost password?': 'zapomněl jste heslo?',
|
||||
'Manage': 'Manage',
|
||||
'Manage Cache': 'Manage Cache',
|
||||
'Menu Model': 'Model rozbalovací nabídky',
|
||||
'Models': 'Modely',
|
||||
'models': 'modely',
|
||||
'Modified By': 'Změněno - kým',
|
||||
'Modified On': 'Změněno - kdy',
|
||||
'Modules': 'Moduly',
|
||||
'modules': 'moduly',
|
||||
'My Sites': 'Správa aplikací',
|
||||
'Name': 'Jméno',
|
||||
'new application "%s" created': 'nová aplikace "%s" vytvořena',
|
||||
'New Application Wizard': 'Nový průvodce aplikací',
|
||||
'New application wizard': 'Nový průvodce aplikací',
|
||||
'New password': 'Nové heslo',
|
||||
'New Record': 'Nový záznam',
|
||||
'new record inserted': 'nový záznam byl založen',
|
||||
'New simple application': 'Vytvořit primitivní aplikaci',
|
||||
'next': 'next',
|
||||
'next 100 rows': 'dalších 100 řádků',
|
||||
'No databases in this application': 'V této aplikaci nejsou žádné databáze',
|
||||
'No Interaction yet': 'Ještě žádná interakce nenastala',
|
||||
'No ticket_storage.txt found under /private folder': 'Soubor ticket_storage.txt v adresáři /private nenalezen',
|
||||
'Object or table name': 'Objekt či tabulka',
|
||||
'Old password': 'Původní heslo',
|
||||
'online designer': 'online návrhář',
|
||||
'Online examples': 'Příklady online',
|
||||
'Open new app in new window': 'Open new app in new window',
|
||||
'or alternatively': 'or alternatively',
|
||||
'Or Get from URL:': 'Or Get from URL:',
|
||||
'or import from csv file': 'nebo importovat z .csv souboru',
|
||||
'Origin': 'Původ',
|
||||
'Original/Translation': 'Originál/Překlad',
|
||||
'Other Plugins': 'Ostatní moduly',
|
||||
'Other Recipes': 'Ostatní zásuvné moduly',
|
||||
'Overview': 'Přehled',
|
||||
'Overwrite installed app': 'Přepsat instalovanou aplikaci',
|
||||
'Pack all': 'Zabalit',
|
||||
'Pack compiled': 'Zabalit zkompilované',
|
||||
'pack plugin': 'pack plugin',
|
||||
'password': 'heslo',
|
||||
'Password': 'Heslo',
|
||||
"Password fields don't match": 'Hesla se neshodují',
|
||||
'Peeking at file': 'Peeking at file',
|
||||
'Please': 'Prosím',
|
||||
'Plugin "%s" in application': 'Plugin "%s" in application',
|
||||
'plugins': 'zásuvné moduly',
|
||||
'Plugins': 'Zásuvné moduly',
|
||||
'Plural Form #%s': 'Plural Form #%s',
|
||||
'Plural-Forms:': 'Množná čísla:',
|
||||
'Powered by': 'Poháněno',
|
||||
'Preface': 'Předmluva',
|
||||
'previous 100 rows': 'předchozích 100 řádků',
|
||||
'Private files': 'Soukromé soubory',
|
||||
'private files': 'soukromé soubory',
|
||||
'profile': 'profil',
|
||||
'Project Progress': 'Vývoj projektu',
|
||||
'Python': 'Python',
|
||||
'Query:': 'Dotaz:',
|
||||
'Quick Examples': 'Krátké příklady',
|
||||
'RAM': 'RAM',
|
||||
'RAM Cache Keys': 'Klíče RAM Cache',
|
||||
'Ram Cleared': 'RAM smazána',
|
||||
'Readme': 'Nápověda',
|
||||
'Recipes': 'Postupy jak na to',
|
||||
'Record': 'Záznam',
|
||||
'record does not exist': 'záznam neexistuje',
|
||||
'Record ID': 'ID záznamu',
|
||||
'Record id': 'id záznamu',
|
||||
'refresh': 'obnovte',
|
||||
'register': 'registrovat',
|
||||
'Register': 'Zaregistrovat se',
|
||||
'Registration identifier': 'Registrační identifikátor',
|
||||
'Registration key': 'Registrační klíč',
|
||||
'reload': 'reload',
|
||||
'Reload routes': 'Znovu nahrát cesty',
|
||||
'Remember me (for 30 days)': 'Zapamatovat na 30 dní',
|
||||
'Remove compiled': 'Odstranit zkompilované',
|
||||
'Removed Breakpoint on %s at line %s': 'Bod přerušení smazán - soubor %s na řádce %s',
|
||||
'Replace': 'Zaměnit',
|
||||
'Replace All': 'Zaměnit vše',
|
||||
'request': 'request',
|
||||
'Reset Password key': 'Reset registračního klíče',
|
||||
'response': 'response',
|
||||
'restart': 'restart',
|
||||
'restore': 'obnovit',
|
||||
'Retrieve username': 'Získat přihlašovací jméno',
|
||||
'return': 'return',
|
||||
'revert': 'vrátit se k původnímu',
|
||||
'Role': 'Role',
|
||||
'Rows in Table': 'Záznamy v tabulce',
|
||||
'Rows selected': 'Záznamů zobrazeno',
|
||||
'rules are not defined': 'pravidla nejsou definována',
|
||||
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Spustí testy v tomto souboru (ke spuštění všech testů, použijte tlačítko 'test')",
|
||||
'Running on %s': 'Běží na %s',
|
||||
'Save': 'Uložit',
|
||||
'Save file:': 'Save file:',
|
||||
'Save via Ajax': 'Uložit pomocí Ajaxu',
|
||||
'Saved file hash:': 'hash uloženého souboru:',
|
||||
'Semantic': 'Modul semantic',
|
||||
'Services': 'Služby',
|
||||
'session': 'session',
|
||||
'session expired': 'session expired',
|
||||
'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s',
|
||||
'shell': 'příkazová řádka',
|
||||
'Singular Form': 'Singular Form',
|
||||
'Site': 'Správa aplikací',
|
||||
'Size of cache:': 'Velikost cache:',
|
||||
'skip to generate': 'skip to generate',
|
||||
'Sorry, could not find mercurial installed': 'Bohužel mercurial není nainstalován.',
|
||||
'Start a new app': 'Vytvořit novou aplikaci',
|
||||
'Start searching': 'Začít hledání',
|
||||
'Start wizard': 'Spustit průvodce',
|
||||
'state': 'stav',
|
||||
'Static': 'Static',
|
||||
'static': 'statické soubory',
|
||||
'Static files': 'Statické soubory',
|
||||
'Statistics': 'Statistika',
|
||||
'Step': 'Step',
|
||||
'step': 'step',
|
||||
'stop': 'stop',
|
||||
'Stylesheet': 'CSS styly',
|
||||
'submit': 'odeslat',
|
||||
'Submit': 'Odeslat',
|
||||
'successful': 'úspěšně',
|
||||
'Support': 'Podpora',
|
||||
'Sure you want to delete this object?': 'Opravdu chcete smazat tento objekt?',
|
||||
'Table': 'tabulka',
|
||||
'Table name': 'Název tabulky',
|
||||
'Temporary': 'Dočasný',
|
||||
'test': 'test',
|
||||
'Testing application': 'Testing application',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Dotaz" je podmínka, například "db.tabulka1.pole1==\'hodnota\'". Podmínka "db.tabulka1.pole1==db.tabulka2.pole2" pak vytvoří SQL JOIN.',
|
||||
'The application logic, each URL path is mapped in one exposed function in the controller': 'Logika aplikace: každá URL je mapována na funkci vystavovanou kontrolérem.',
|
||||
'The Core': 'Jádro (The Core)',
|
||||
'The data representation, define database tables and sets': 'Reprezentace dat: definovat tabulky databáze a záznamy',
|
||||
'The output of the file is a dictionary that was rendered by the view %s': 'Výstup ze souboru je slovník, který se zobrazil v pohledu %s.',
|
||||
'The presentations layer, views are also known as templates': 'Prezentační vrstva: pohledy či templaty (šablony)',
|
||||
'The Views': 'Pohledy (The Views)',
|
||||
'There are no controllers': 'There are no controllers',
|
||||
'There are no modules': 'There are no modules',
|
||||
'There are no plugins': 'Žádné moduly nejsou instalovány.',
|
||||
'There are no private files': 'Žádné soukromé soubory neexistují.',
|
||||
'There are no static files': 'There are no static files',
|
||||
'There are no translators, only default language is supported': 'There are no translators, only default language is supported',
|
||||
'There are no views': 'There are no views',
|
||||
'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.',
|
||||
'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.',
|
||||
'This App': 'Tato aplikace',
|
||||
'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.',
|
||||
'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk',
|
||||
'This is the %(filename)s template': 'This is the %(filename)s template',
|
||||
'this page to see if a breakpoint was hit and debug interaction is required.': 'tuto stránku, abyste uviděli, zda se dosáhlo bodu přerušení.',
|
||||
'Ticket': 'Ticket',
|
||||
'Ticket ID': 'Ticket ID',
|
||||
'Time in Cache (h:m:s)': 'Čas v Cache (h:m:s)',
|
||||
'Timestamp': 'Časové razítko',
|
||||
'to previous version.': 'k předchozí verzi.',
|
||||
'To create a plugin, name a file/folder plugin_[name]': 'Zásuvný modul vytvoříte tak, že pojmenujete soubor/adresář plugin_[jméno modulu]',
|
||||
'To emulate a breakpoint programatically, write:': 'K nastavení bodu přerušení v kódu programu, napište:',
|
||||
'to use the debugger!': ', abyste mohli ladící program používat!',
|
||||
'toggle breakpoint': 'vyp./zap. bod přerušení',
|
||||
'Toggle Fullscreen': 'Na celou obrazovku a zpět',
|
||||
'too short': 'Příliš krátké',
|
||||
'Traceback': 'Traceback',
|
||||
'Translation strings for the application': 'Překlad textů pro aplikaci',
|
||||
'try something like': 'try something like',
|
||||
'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení',
|
||||
'try view': 'try view',
|
||||
'Twitter': 'Twitter',
|
||||
'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.',
|
||||
'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.',
|
||||
'Unable to check for upgrades': 'Unable to check for upgrades',
|
||||
'unable to parse csv file': 'csv soubor nedá sa zpracovat',
|
||||
'uncheck all': 'vše odznačit',
|
||||
'Uninstall': 'Odinstalovat',
|
||||
'update': 'aktualizovat',
|
||||
'update all languages': 'aktualizovat všechny jazyky',
|
||||
'Update:': 'Upravit:',
|
||||
'Upgrade': 'Upgrade',
|
||||
'upgrade now': 'upgrade now',
|
||||
'upgrade now to %s': 'upgrade now to %s',
|
||||
'upload': 'nahrát',
|
||||
'Upload': 'Upload',
|
||||
'Upload a package:': 'Nahrát balík:',
|
||||
'Upload and install packed application': 'Nahrát a instalovat zabalenou aplikaci',
|
||||
'upload file:': 'nahrát soubor:',
|
||||
'upload plugin file:': 'nahrát soubor modulu:',
|
||||
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použijte (...)&(...) pro AND, (...)|(...) pro OR a ~(...) pro NOT pro sestavení složitějších dotazů.',
|
||||
'User %(id)s Logged-in': 'Uživatel %(id)s přihlášen',
|
||||
'User %(id)s Logged-out': 'Uživatel %(id)s odhlášen',
|
||||
'User %(id)s Password changed': 'Uživatel %(id)s změnil heslo',
|
||||
'User %(id)s Profile updated': 'Uživatel %(id)s upravil profil',
|
||||
'User %(id)s Registered': 'Uživatel %(id)s se zaregistroval',
|
||||
'User %(id)s Username retrieved': 'Uživatel %(id)s si nachal zaslat přihlašovací jméno',
|
||||
'User ID': 'ID uživatele',
|
||||
'Username': 'Přihlašovací jméno',
|
||||
'variables': 'variables',
|
||||
'Verify Password': 'Zopakujte heslo',
|
||||
'Version': 'Verze',
|
||||
'Version %s.%s.%s (%s) %s': 'Verze %s.%s.%s (%s) %s',
|
||||
'Versioning': 'Verzování',
|
||||
'Videos': 'Videa',
|
||||
'View': 'Pohled (View)',
|
||||
'Views': 'Pohledy',
|
||||
'views': 'pohledy',
|
||||
'Web Framework': 'Web Framework',
|
||||
'web2py is up to date': 'Máte aktuální verzi web2py.',
|
||||
'web2py online debugger': 'Ladící online web2py program',
|
||||
'web2py Recent Tweets': 'Štěbetání na Twitteru o web2py',
|
||||
'web2py upgrade': 'web2py upgrade',
|
||||
'web2py upgraded; please restart it': 'web2py upgraded; please restart it',
|
||||
'Welcome': 'Vítejte',
|
||||
'Welcome to web2py': 'Vitejte ve web2py',
|
||||
'Welcome to web2py!': 'Vítejte ve web2py!',
|
||||
'Which called the function %s located in the file %s': 'která zavolala funkci %s v souboru (kontroléru) %s.',
|
||||
'You are successfully running web2py': 'Úspěšně jste spustili web2py.',
|
||||
'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Nastavovat a mazat body přerušení je též možno v rámci editování zdrojového souboru přes tlačítko Vyp./Zap. bod přerušení',
|
||||
'You can modify this application and adapt it to your needs': 'Tuto aplikaci si můžete upravit a přizpůsobit ji svým potřebám.',
|
||||
'You need to set up and reach a': 'Je třeba nejprve nastavit a dojít až na',
|
||||
'You visited the url %s': 'Navštívili jste stránku %s,',
|
||||
'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Aplikace bude blokována než se klikne na jedno z tlačítek (další, krok, pokračovat, atd.)',
|
||||
'You can inspect variables using the console bellow': 'Níže pomocí příkazové řádky si můžete prohlédnout proměnné',
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
'unable to uninstall "%s"': 'impossibile disinstallare "%s"',
|
||||
'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"',
|
||||
'uncheck all': 'smarca tutti',
|
||||
'Uninstall': 'disinstalla',
|
||||
'Uninstall': 'Disinstalla',
|
||||
'update': 'aggiorna',
|
||||
'update all languages': 'aggiorna tutti i linguaggi',
|
||||
'Update:': 'Aggiorna:',
|
||||
@@ -348,7 +348,7 @@
|
||||
'Versioning': 'Versioning',
|
||||
'View': 'Vista',
|
||||
'view': 'vista',
|
||||
'Views': 'viste',
|
||||
'Views': 'Viste',
|
||||
'views': 'viste',
|
||||
'Web Framework': 'Web Framework',
|
||||
'web2py is up to date': 'web2py è aggiornato',
|
||||
|
||||
@@ -47,5 +47,3 @@ if 'adminLanguage' in request.cookies and not (request.cookies['adminLanguage']
|
||||
#set static_version
|
||||
from gluon.settings import global_settings
|
||||
response.static_version = global_settings.web2py_version.split('-')[0]
|
||||
|
||||
|
||||
|
||||
@@ -34,4 +34,3 @@ else:
|
||||
URL(_a, 'default', f='logout')))
|
||||
response.menu.append((T('Debug'), False,
|
||||
URL(_a, 'debug', 'interact')))
|
||||
|
||||
|
||||
@@ -7,11 +7,10 @@ def stateWidget(field, value, data={'on-label':'Enabled', 'off-label':'Disabled'
|
||||
except:
|
||||
fieldName = field
|
||||
|
||||
div = DIV(INPUT( _type='checkbox', _name='%s' % fieldName, _checked= 'checked' if value == 'true' else None, _value='true'),
|
||||
_class='make-bootstrap-switch',
|
||||
div = DIV(INPUT( _type='checkbox', _name='%s' % fieldName, _checked= 'checked' if value == 'true' else None, _value='true'),
|
||||
_class='make-bootstrap-switch',
|
||||
data=data)
|
||||
script = SCRIPT("""
|
||||
jQuery(".make-bootstrap-switch input[name='%s']").parent().bootstrapSwitch();
|
||||
""" % fieldName)
|
||||
return DIV(div, script)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<tr>
|
||||
<td><a class="btn btn-180 btn-success" href="https://dl.dropbox.com/u/18065445/web2py/web2py_manual_5th.pdf">Manual</a></td>
|
||||
<td><a class="btn btn-180" href="{{=URL('changelog')}}">Change Log</a></td>
|
||||
<td><a class="btn btn-180" href="http://code.google.com/p/web2py/issues/list">Report a Bug</a></td>
|
||||
<td><a class="btn btn-180" href="https://github.com/web2py/web2py/issues">Report a Bug</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'!langcode!': 'ca',
|
||||
'!langname!': 'Català',
|
||||
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"actualizi" és una expressió opcional com "camp1=\'nou_valor\'". No es poden actualitzar o eliminar resultats de un JOIN',
|
||||
'%(nrows)s records found': '%(nrows)s registres trobats',
|
||||
'%s %%{position}': '%s %%{posició}',
|
||||
'%s %%{row} deleted': '%s %%{fila} %%{eliminada}',
|
||||
'%s %%{row} updated': '%s %%{fila} %%{actualitzada}',
|
||||
'%s selected': '%s %%{seleccionat}',
|
||||
'%Y-%m-%d': '%d/%m/%Y',
|
||||
'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S',
|
||||
'(something like "it-it")': '(similar a "això-això")',
|
||||
'@markmin\x01An error occured, please [[reload %s]] the page': 'Hi ha hagut un error, si us plau [[recarregui %s]] la pàgina',
|
||||
'@markmin\x01Number of entries: **%s**': "Nombre d'entrades: **%s**",
|
||||
'A new version of web2py is available': 'Hi ha una nova versió de wep2py disponible',
|
||||
'A new version of web2py is available: %s': 'Hi ha una nova versió de wep2py disponible: %s',
|
||||
'About': 'Sobre',
|
||||
'about': 'sobre',
|
||||
'About application': "Sobre l'aplicació",
|
||||
'Access Control': "Control d'Accés",
|
||||
'Add': 'Afegir',
|
||||
'Add Record': 'Afegeix registre',
|
||||
'additional code for your application': '`codi addicional per a la seva aplicació',
|
||||
'admin disabled because no admin password': 'admin inhabilitat per falta de contrasenya',
|
||||
'admin disabled because not supported on google app engine': 'admin inhabilitat, no és suportat en GAE',
|
||||
'admin disabled because unable to access password file': 'admin inhabilitat, impossible accedir al fitxer con la contrasenya',
|
||||
'Admin is disabled because insecure channel': 'Admin inhabilitat, el canal no és segur',
|
||||
'Admin is disabled because unsecure channel': 'Admin inhabilitat, el canal no és segur',
|
||||
'Administrative interface': 'Interfície administrativa',
|
||||
'Administrative Interface': 'Interfície Administrativa',
|
||||
'administrative interface': 'interfície administrativa',
|
||||
'Administrator Password:': 'Contrasenya del Administrador:',
|
||||
'Ajax Recipes': 'Receptes AJAX',
|
||||
'An error occured, please %s the page': 'Hi ha hagut un error, per favor %s la pàgina',
|
||||
'And': 'I',
|
||||
'and rename it (required):': 'i renombri-la (requerit):',
|
||||
'and rename it:': " i renombri'l:",
|
||||
'appadmin': 'appadmin',
|
||||
'appadmin is disabled because insecure channel': 'admin inhabilitat, el canal no és segur',
|
||||
'application "%s" uninstalled': 'aplicació "%s" desinstal·lada',
|
||||
'application compiled': 'aplicació compilada',
|
||||
'application is compiled and cannot be designed': 'la aplicació està compilada i no pot ser modificada',
|
||||
'Apply changes': 'Aplicar canvis',
|
||||
'Appointment': 'Nomenament',
|
||||
'Are you sure you want to delete file "%s"?': 'Està segur que vol eliminar el arxiu "%s"?',
|
||||
'Are you sure you want to delete this object?': 'Està segur que vol esborrar aquest objecte?',
|
||||
'Are you sure you want to uninstall application "%s"': '¿Està segur que vol desinstalar la aplicació "%s"',
|
||||
'Are you sure you want to uninstall application "%s"?': '¿Està segur que vol desinstalar la aplicació "%s"?',
|
||||
'at': 'a',
|
||||
'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENCIÓ: Inici de sessió requereix una connexió segura (HTTPS) o localhost.',
|
||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.',
|
||||
'ATTENTION: you cannot edit the running application!': 'ATENCIO: no pot modificar la aplicació que està ejecutant-se!',
|
||||
'Authentication': 'Autenticació',
|
||||
'Authentication failed at client DB!': '¡La autenticació ha fallat en la BDD client!',
|
||||
'Authentication failed at main DB!': '¡La autenticació ha fallat en la BDD principal!',
|
||||
'Available Databases and Tables': 'Bases de dades i taules disponibles',
|
||||
'Back': 'Endarrera',
|
||||
'Buy this book': 'Compra aquest lllibre',
|
||||
'Cache': 'Caché',
|
||||
'cache': 'caché',
|
||||
'Cache Cleared': 'Caché Netejada',
|
||||
'Cache Keys': 'Claus de la Caché',
|
||||
'cache, errors and sessions cleaned': 'caché, errors i sessions eliminats',
|
||||
'Cannot be empty': 'No pot estar buit',
|
||||
'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se pot compilar: hi ha errors en la seva aplicació. Depuri, corregeixi errors i torni a intentar-ho.',
|
||||
'cannot upload file "%(filename)s"': 'no és possible pujar fitxer "%(filename)s"',
|
||||
'Change Password': 'Canviï la Contrasenya',
|
||||
'Change password': 'Canviï la contrasenya',
|
||||
'change password': 'canviï la contrasenya',
|
||||
'Changelog': 'Changelog',
|
||||
'check all': 'marcar tots',
|
||||
'Check to delete': 'Marqui per a eliminar',
|
||||
'choose one': 'escolliu un',
|
||||
'clean': 'neteja',
|
||||
'Clear': 'Netejar',
|
||||
'Clear CACHE?': 'Netejar Memòrica Cau?',
|
||||
'Clear DISK': 'Netejar DISC',
|
||||
'Clear RAM': 'Netejar RAM',
|
||||
'Click on the link %(link)s to reset your password': "Cliqui en l'enllaç %(link)s per a reiniciar la seva contrasenya",
|
||||
'click to check for upgrades': 'feu clic per buscar actualitzacions',
|
||||
'client': 'cliente',
|
||||
'Client IP': 'IP del Client',
|
||||
'Close': 'Tancar',
|
||||
'Comma-separated export including columns not shown; fields from other tables are exported as raw values for faster export': 'Comma-separated export including columns not shown; fields from other tables are exported as raw values for faster export',
|
||||
'Comma-separated export of visible columns. Fields from other tables are exported as they appear on-screen but this may be slow for many rows': 'Comma-separated export of visible columns. Fields from other tables are exported as they appear on-screen but this may be slow for many rows',
|
||||
'Community': 'Comunitat',
|
||||
'compile': 'compilar',
|
||||
'compiled application removed': 'aplicació compilada eliminada',
|
||||
'Components and Plugins': 'Components i Plugins',
|
||||
'contains': 'conté',
|
||||
'Controller': 'Controlador',
|
||||
'Controllers': 'Controladors',
|
||||
'controllers': 'controladors',
|
||||
'Copyright': 'Copyright',
|
||||
'Correo electrónico invàlid': 'Correu electrònic invàlid',
|
||||
'create file with filename:': 'crear el fitxer amb el nom:',
|
||||
'Create new application': 'Crear una nova aplicació',
|
||||
'create new application:': 'crear una nova aplicació:',
|
||||
'Create New Page': 'Crear Pàgina Nova',
|
||||
'Create Page from Slug': 'Create Page from Slug',
|
||||
'Created By': 'Creat Per',
|
||||
'Created On': 'Creat a',
|
||||
'CSV': 'CSV',
|
||||
'CSV (hidden cols)': 'CSV (columnas ocultes)',
|
||||
'Current request': 'Sol·licitud en curs',
|
||||
'Current response': 'Resposta en curs',
|
||||
'Current session': 'Sessió en curs',
|
||||
'currently saved or': 'actualment guardat o',
|
||||
'customize me!': "¡Adapta'm!",
|
||||
'data uploaded': 'dades pujades',
|
||||
'Database': 'Base de dades',
|
||||
'Database %s select': 'selecció a base de dades %s',
|
||||
'database administration': 'administració de base de dades',
|
||||
'Database Administration (appadmin)': 'Administració de Base de Dades (appadmin)',
|
||||
'Date and Time': 'Data i Hora',
|
||||
'DB': 'BDD',
|
||||
'db': 'bdd',
|
||||
'DB Model': 'Model BDD',
|
||||
'defines tables': 'defineix taules',
|
||||
'Delete': 'Eliminar',
|
||||
'delete': 'eliminar',
|
||||
'delete all checked': 'eliminar marcats',
|
||||
'Delete:': 'Eliminar:',
|
||||
'Demo': 'Demostració',
|
||||
'Deploy on Google App Engine': 'Desplegament a Google App Engine',
|
||||
'Deployment Recipes': 'Receptes de desplegament',
|
||||
'Description': 'Descripció',
|
||||
'design': 'diseny',
|
||||
'DESIGN': 'DISENY',
|
||||
'Design for': 'Diseny per a',
|
||||
'detecting': 'detectant',
|
||||
'DISK': 'DISC',
|
||||
'Disk Cache Keys': 'Claus de Caché en Disc',
|
||||
'Disk Cleared': 'Disc netejat',
|
||||
'Documentation': 'Documentació',
|
||||
"Don't know what to do?": 'No sap què fer?',
|
||||
'done!': '¡fet!',
|
||||
'Download': 'Descàrregues',
|
||||
'E-mail': 'Correu electrònic',
|
||||
'edit': 'editar',
|
||||
'EDIT': 'EDITAR',
|
||||
'Edit': 'Editar',
|
||||
'Edit application': 'Editar aplicació',
|
||||
'edit controller': 'editar controlador',
|
||||
'Edit current record': 'Editar el registre actual',
|
||||
'Edit Menu': 'Editar Menu',
|
||||
'Edit Page': 'Editar Pàgina',
|
||||
'Edit Page Media': 'Edit Page Media',
|
||||
'Edit Profile': 'Editar Perfil',
|
||||
'edit profile': 'editar perfil',
|
||||
'Edit This App': 'Editi aquesta App',
|
||||
'Editing file': 'Editant fitxer',
|
||||
'Editing file "%s"': 'Editant fitxer "%s"',
|
||||
'El fitxer ha de ser PDF': 'El fitxer ha de ser PDF',
|
||||
'El fitxer ha de ser PDF o XML': 'El fitxer ha de ser PDF o XML',
|
||||
'Email': 'Email',
|
||||
'Email and SMS': 'Correu electrònic i SMS',
|
||||
'Email sent': 'Correu electrònic enviat',
|
||||
'End of impersonation': 'Fi de suplantació',
|
||||
'enter a number between %(min)g and %(max)g': 'introdueixi un número entre %(min)g i %(max)g',
|
||||
'Enter a valid email address': 'Entri una adreça email vàlida',
|
||||
'enter a value': 'entri un valor',
|
||||
'Enter a value': 'Entri un valor',
|
||||
'Enter an integer between %(min)g and %(max)g': 'Entri un numero enter entre %(min)g i %(max)g',
|
||||
'enter an integer between %(min)g and %(max)g': 'entri numero enter entre %(min)g i %(max)g',
|
||||
'enter date and time as %(format)s': 'entri data i hora com %(format)s',
|
||||
'Enter from %(min)g to %(max)g characters': 'Entri des de %(min)g a %(max)g caràcters',
|
||||
'Enter valid filename': 'Entri nom de fitxer vàlid',
|
||||
'Error logs for "%(app)s"': 'Bitàcora de errors a "%(app)s"',
|
||||
'errors': 'errors',
|
||||
'Errors': 'Errors',
|
||||
'Errors in form, please check it out.': 'Hi ha errors en el formulari, per favor comprovi-ho.',
|
||||
'export as csv file': 'exportar com fitxer CSV',
|
||||
'Export:': 'Exportar:',
|
||||
'exposes': 'exposa',
|
||||
'extends': 'extén',
|
||||
'failed to reload module': 'la recàrrega del mòdul ha fallat',
|
||||
'FAQ': 'FAQ',
|
||||
'file': 'fitxer',
|
||||
'file "%(filename)s" created': 'fitxer "%(filename)s" creat',
|
||||
'file "%(filename)s" deleted': 'fitxer "%(filename)s" eliminat',
|
||||
'file "%(filename)s" uploaded': 'fitxer "%(filename)s" pujat',
|
||||
'file "%(filename)s" was not deleted': 'fitxer "%(filename)s" no fou eliminat',
|
||||
'file "%s" of %s restored': 'fitxer "%s" de %s restaurat',
|
||||
'file ## download': 'file ',
|
||||
'file changed on disk': 'fitxer modificat en el disco',
|
||||
'file does not exist': 'fitxer no existeix',
|
||||
'file saved on %(time)s': 'fitxer guardat a %(time)s',
|
||||
'file saved on %s': 'fitxer guardat a %s',
|
||||
'First name': 'Nom',
|
||||
'Forgot username?': 'Ha oblidat el nom de usuari?',
|
||||
'Forms and Validators': 'Formularis i validadors',
|
||||
'Free Applications': 'Aplicacions Lliures',
|
||||
'Functions with no doctests will result in [passed] tests.': 'Funcions sense doctests equivalen a pruebas [aceptades].',
|
||||
'Group %(group_id)s created': 'Grupo %(group_id)s creat',
|
||||
'Group ID': 'ID de Grup',
|
||||
'Group uniquely assigned to user %(id)s': 'Grup assignat únicament al usuari %(id)s',
|
||||
'Groups': 'Grups',
|
||||
'Hello': 'Hola',
|
||||
'Hello World': 'Hola Món',
|
||||
'help': 'ajuda',
|
||||
'Home': 'Inici',
|
||||
'Hosted by': 'Hosted by',
|
||||
'How did you get here?': 'Com has arribat aquí?',
|
||||
'HTML': 'HTML',
|
||||
'HTML export of visible columns': 'HTML export de columnes visibles',
|
||||
'htmledit': 'htmledit',
|
||||
'Impersonate': 'Suplantar',
|
||||
'import': 'importar',
|
||||
'Import/Export': 'Importar/Exportar',
|
||||
'in': 'a',
|
||||
'includes': 'inclou',
|
||||
'Index': 'Índex',
|
||||
'insert new': 'inserti nou',
|
||||
'insert new %s': 'inserti nou %s',
|
||||
'Installed applications': 'Aplicacions instalades',
|
||||
'Insufficient privileges': 'Privilegis insuficients',
|
||||
'internal error': 'error intern',
|
||||
'Internal State': 'Estat Intern',
|
||||
'Introduction': 'Introducció',
|
||||
'Invalid action': 'Acció invàlida',
|
||||
'Invalid email': 'Correo electrónico invàlid',
|
||||
'invalid expression': 'expressió invàlida',
|
||||
'Invalid login': 'Inici de sessió invàlida',
|
||||
'invalid password': 'contrasenya invàlida',
|
||||
'Invalid Query': 'Consulta invàlida',
|
||||
'invalid request': 'sol·licitud invàlida',
|
||||
'Invalid reset password': 'Reinici de contrasenya invàlid',
|
||||
'invalid ticket': 'tiquet invàlid',
|
||||
'Is Active': 'Està Actiu',
|
||||
'Key': 'Clau',
|
||||
'language file "%(filename)s" created/updated': 'fitxer de llenguatge "%(filename)s" creat/actualitzat',
|
||||
'Language files (static strings) updated': 'Fitxers de llenguatge (cadenes estàtiques) actualitzats',
|
||||
'languages': 'llenguatges',
|
||||
'Languages': 'Llenguatges',
|
||||
'languages updated': 'llenguatges actualitzats',
|
||||
'Last name': 'Cognom',
|
||||
'Last saved on:': 'Guardat a:',
|
||||
'Layout': 'Diseny de pàgina',
|
||||
'Layout Plugins': 'Plugins de disseny',
|
||||
'Layouts': 'Dissenys de pàgines',
|
||||
'License for': 'Llicència per a',
|
||||
'Live Chat': 'Xat en viu',
|
||||
'loading...': 'carregant...',
|
||||
'Log In': 'Log In',
|
||||
'Log Out': 'Log Out',
|
||||
'Logged in': 'Sessió iniciada',
|
||||
'Logged out': 'Sessió finalitzada',
|
||||
'Login': 'Inici de sessió',
|
||||
'login': 'inici de sessió',
|
||||
'Login disabled by administrator': 'Inici de sessió inhabilitat pel administrador',
|
||||
'Login to the Administrative Interface': 'Inici de sessió per a la Interfície Administrativa',
|
||||
'logout': 'fi de sessió',
|
||||
'Logout': 'Fi de sessió',
|
||||
'Lost Password': 'Contrasenya perdida',
|
||||
'Lost password?': 'Ha oblidat la contrasenya?',
|
||||
'lost password?': '¿ha oblidat la contrasenya?',
|
||||
'Main Menu': 'Menú principal',
|
||||
'Manage %(action)s': 'Manage %(action)s',
|
||||
'Manage Access Control': 'Manage Access Control',
|
||||
'Manage Cache': 'Gestionar la Caché',
|
||||
'Menu Model': 'Model "menu"',
|
||||
'merge': 'combinar',
|
||||
'Models': 'Models',
|
||||
'models': 'models',
|
||||
'Modified By': 'Modificat Per',
|
||||
'Modified On': 'Modificat A',
|
||||
'Modules': 'Mòduls',
|
||||
'modules': 'mòduls',
|
||||
'must be YYYY-MM-DD HH:MM:SS!': '¡debe ser DD/MM/YYYY HH:MM:SS!',
|
||||
'must be YYYY-MM-DD!': '¡debe ser DD/MM/YYYY!',
|
||||
'My Sites': 'Els Meus Llocs',
|
||||
'Name': 'Nombre',
|
||||
'New': 'Nuevo',
|
||||
'New %(entity)s': 'Nou %(entity)s',
|
||||
'new application "%s" created': 'nova aplicació "%s" creada',
|
||||
'New password': 'Contrasenya nova',
|
||||
'New Record': 'Registre nou',
|
||||
'new record inserted': 'nou registre insertat',
|
||||
'New Search': 'Cerca nova',
|
||||
'next %s rows': 'següents %s files',
|
||||
'next 100 rows': '100 files següents',
|
||||
'NO': 'NO',
|
||||
'No databases in this application': 'No hi ha bases de dades en esta aplicació',
|
||||
'No records found': "No s'han trobat registres",
|
||||
'Not authorized': 'No autoritzat',
|
||||
'not in': 'no a',
|
||||
'Object or table name': 'Nom del objecte o taula',
|
||||
'Old password': 'Contrasenya anterior',
|
||||
'Online examples': 'Ejemples en línia',
|
||||
'Or': 'O',
|
||||
'or import from csv file': 'o importar desde fitxer CSV',
|
||||
'or provide application url:': 'o proveeix URL de la aplicació:',
|
||||
'Origin': 'Origen',
|
||||
'Original/Translation': 'Original/Traducció',
|
||||
'Other Plugins': 'Altres Plugins',
|
||||
'Other Recipes': 'Altres Receptes',
|
||||
'Overview': 'Resum',
|
||||
'pack all': 'empaquetar tot',
|
||||
'pack compiled': 'empaquetar compilats',
|
||||
'Password': 'Contrasenya',
|
||||
'Password changed': 'Contrasenya cambiada',
|
||||
"Password fields don't match": 'Els camps de contrasenya no coincideixen',
|
||||
'Password reset': 'Reinici de contrasenya',
|
||||
'Peeking at file': 'Visualitzant fitxer',
|
||||
'Permission': 'Permís',
|
||||
'Permissions': 'Permisos',
|
||||
'Phone': 'Telèfon',
|
||||
'please input your password again': 'si us plau, entri un altre cop la seva contrasenya',
|
||||
'Plugins': 'Plugins',
|
||||
'Powered by': 'Aquest lloc utilitza',
|
||||
'Preface': 'Prefaci',
|
||||
'Presentar Factures': 'Presentar Factures',
|
||||
'Presentar factures': 'Presentar factures',
|
||||
'previous %s rows': '%s files prèvies',
|
||||
'previous 100 rows': '100 files anteriors',
|
||||
'Profile': 'Perfil',
|
||||
'Profile updated': 'Perfil actualitzat',
|
||||
'pygraphviz library not found': 'pygraphviz library not found',
|
||||
'Python': 'Python',
|
||||
'Query Not Supported: %s': 'Consulta No Suportada: %s',
|
||||
'Query:': 'Consulta:',
|
||||
'Quick Examples': 'Exemple Ràpids',
|
||||
'RAM': 'RAM',
|
||||
'RAM Cache Keys': 'Claus de la Caché en RAM',
|
||||
'Ram Cleared': 'Ram Netjeda',
|
||||
'Recipes': 'Receptes',
|
||||
'Record': 'Registre',
|
||||
'Record %(id)s created': 'Registre %(id)s creat',
|
||||
'Record Created': 'Registre Creat',
|
||||
'record does not exist': 'el registre no existe',
|
||||
'Record ID': 'ID de Registre',
|
||||
'Record id': 'Id de registre',
|
||||
'Ref APB': 'Ref APB',
|
||||
'register': "registri's",
|
||||
'Register': "Registri's",
|
||||
'Registration identifier': 'Identificador de Registre',
|
||||
'Registration key': 'Clau de registre',
|
||||
'Registration successful': 'Registre amb èxit',
|
||||
'reload': 'recarregar',
|
||||
'Remember me (for 30 days)': "Recordi'm (durant 30 dies)",
|
||||
'remove compiled': 'eliminar compilades',
|
||||
'Request reset password': 'Sol·licitud de restabliment de contrasenya',
|
||||
'Reset password': 'Reiniciar contrasenya',
|
||||
'Reset Password key': 'Restaurar Clau de la Contrasenya',
|
||||
'Resolve Conflict file': 'Resolgui el Conflicte de fitxer',
|
||||
'restore': 'restaurar',
|
||||
'Retrieve username': 'Recuperar nom de usuari',
|
||||
'revert': 'revertir',
|
||||
'Role': 'Rol',
|
||||
'Roles': 'Rols',
|
||||
'Rows in Table': 'Files a la taula',
|
||||
'Rows selected': 'Files seleccionades',
|
||||
'save': 'guardar',
|
||||
'Save model as...': 'Save model as...',
|
||||
'Saved file hash:': 'Hash del fitxer guardat:',
|
||||
'Search': 'Buscar',
|
||||
'Search Pages': 'Search Pages',
|
||||
'Semantic': 'Semàntica',
|
||||
'Services': 'Serveis',
|
||||
'session expired': 'sessió expirada',
|
||||
'shell': 'terminal',
|
||||
'Sign Up': 'Sign Up',
|
||||
'site': 'lloc',
|
||||
'Size of cache:': 'Mida de la Caché:',
|
||||
'Slug': 'Slug',
|
||||
'some files could not be removed': 'algunos archivos no pudieron ser removidos',
|
||||
'Spreadsheet-optimised export of tab-separated content including hidden columns. May be slow': 'Spreadsheet-optimised export of tab-separated content including hidden columns. May be slow',
|
||||
'Spreadsheet-optimised export of tab-separated content, visible columns only. May be slow.': 'Spreadsheet-optimised export of tab-separated content, visible columns only. May be slow.',
|
||||
'start': 'inici',
|
||||
'Start building a new search': 'Start building a new search',
|
||||
'starts with': 'comença per',
|
||||
'state': 'estat',
|
||||
'static': 'estàtics',
|
||||
'Static files': 'Fitxers estàtics',
|
||||
'Statistics': 'Estadístiques',
|
||||
'Stylesheet': "Fulla d'estil",
|
||||
'Submit': 'Enviar',
|
||||
'submit': 'enviar',
|
||||
'Success!': 'Correcte!',
|
||||
'Support': 'Suport',
|
||||
'Sure you want to delete this object?': '¿Està segur que vol eliminar aquest objecte?',
|
||||
'Table': 'taula',
|
||||
'Table name': 'Nom de la taula',
|
||||
'test': 'provar',
|
||||
'Testing application': 'Provant aplicació',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La "consulta" és una condición com "db.tabla1.campo1==\'valor\'". Algo com "db.tabla1.campo1==db.tabla2.campo2" resulta en un JOIN SQL.',
|
||||
'the application logic, each URL path is mapped in one exposed function in the controller': 'la lògica de la aplicació, cada ruta URL es mapeja en una funció exposada en el controlador',
|
||||
'The Core': 'El Nucli',
|
||||
'the data representation, define database tables and sets': 'la representació de dades, defineix taules i conjunts de base de dades',
|
||||
'The output of the file is a dictionary that was rendered by the view %s': 'El resultat de aquesta funció és un diccionari que és desplegat per la vista %s',
|
||||
'the presentations layer, views are also known as templates': 'la capa de presentació, les vistes també són anomenades plantilles',
|
||||
'The Views': 'Les Vistes',
|
||||
'There are no controllers': 'No hi ha controladors',
|
||||
'There are no models': 'No hi ha models',
|
||||
'There are no modules': 'No hi ha mòduls',
|
||||
'There are no static files': 'No hi ha fitxers estàtics',
|
||||
'There are no translators, only default language is supported': 'No hi ha traductors, només el llenguatge per defecte és suportat',
|
||||
'There are no views': 'No hi ha vistes',
|
||||
'these files are served without processing, your images go here': 'aquests fitxers són servits sense processar, les seves imatges van aquí',
|
||||
'This App': 'Aquesta Aplicació',
|
||||
'This email already has an account': 'Aquest correu electrònic ja té un compte',
|
||||
'This is a copy of the scaffolding application': 'Aquesta és una còpia de la aplicació de bastiment',
|
||||
'This is the %(filename)s template': 'Aquesta és la plantilla %(filename)s',
|
||||
'Ticket': 'Tiquet',
|
||||
'Time in Cache (h:m:s)': 'Temps en Caché (h:m:s)',
|
||||
'Timestamp': 'Marca de temps',
|
||||
'Title': 'Títol',
|
||||
'to previous version.': 'a la versió prèvia.',
|
||||
'To emulate a breakpoint programatically, write:': 'Emular un punto de ruptura programàticament, escribir:',
|
||||
'to use the debugger!': 'usar el depurador!',
|
||||
'toggle breakpoint': 'alternar punt de ruptura',
|
||||
'Toggle comment': 'Alternar comentari',
|
||||
'Toggle Fullscreen': 'Alternar pantalla completa',
|
||||
'too short': 'massa curt',
|
||||
'Traceback': 'Traceback',
|
||||
'translation strings for the application': 'cadenes de caracters de traducció per a la aplicació',
|
||||
'try': 'intenti',
|
||||
'try something like': 'intenti algo com',
|
||||
'TSV (Excel compatible)': 'TSV (compatible Excel)',
|
||||
'TSV (Excel compatible, hidden cols)': 'TSV (compatible Excel, columnes ocultes)',
|
||||
'TSV (Spreadsheets)': 'TSV (Fulls de càlcul)',
|
||||
'TSV (Spreadsheets, hidden cols)': 'TSV (Fulls de càlcul, columnes amagades)',
|
||||
'Twitter': 'Twitter',
|
||||
'Unable to check for upgrades': 'No és possible verificar la existencia de actualitzacions',
|
||||
'unable to create application "%s"': 'no és possible crear la aplicació "%s"',
|
||||
'unable to delete file "%(filename)s"': 'no és possible eliminar el fitxer "%(filename)s"',
|
||||
'Unable to download': 'No és possible la descàrrega',
|
||||
'Unable to download app': 'No és possible descarregar la aplicació',
|
||||
'unable to parse csv file': 'no és possible analitzar el fitxer CSV',
|
||||
'unable to uninstall "%s"': 'no és possible instalar "%s"',
|
||||
'uncheck all': 'desmarcar tots',
|
||||
'uninstall': 'desinstalar',
|
||||
'unknown': 'desconocido',
|
||||
'update': 'actualitzar',
|
||||
'update all languages': 'actualitzar tots els llenguatges',
|
||||
'Update:': 'Actualizi:',
|
||||
'upload application:': 'pujar aplicació:',
|
||||
'Upload existing application': 'Puji aquesta aplicació',
|
||||
'upload file:': 'puji fitxer:',
|
||||
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, i ~(...) para NOT, para crear consultas més complexes.',
|
||||
'User': 'Usuari',
|
||||
'User %(id)s is impersonating %(other_id)s': 'El usuari %(id)s està suplantant %(other_id)s',
|
||||
'User %(id)s Logged-in': 'El usuari %(id)s inicià la sessió',
|
||||
'User %(id)s Logged-out': 'El usuari %(id)s finalitzà la sessió',
|
||||
'User %(id)s Password changed': 'Contrasenya del usuari %(id)s canviada',
|
||||
'User %(id)s Password reset': 'Contrasenya del usuari %(id)s reiniciada',
|
||||
'User %(id)s Profile updated': 'Actualitzat el perfil del usuari %(id)s',
|
||||
'User %(id)s Registered': 'Usuari %(id)s Registrat',
|
||||
'User %(id)s Username retrieved': 'Se ha recuperat el nom de usuari del usuari %(id)s',
|
||||
'User %(username)s Logged-in': 'El usuari %(username)s inicià la sessió',
|
||||
"User '%(username)s' Logged-in": "El usuari '%(username)s' inicià la sessió",
|
||||
"User '%(username)s' Logged-out": "El usuari '%(username)s' finalitzà la sessió",
|
||||
'User Id': 'Id de Usuari',
|
||||
'User ID': 'ID de Usuari',
|
||||
'User Logged-out': 'El usuari finalitzà la sessió',
|
||||
'Username': 'Nom de usuari',
|
||||
'Username retrieve': 'Recuperar nom de usuari',
|
||||
'Users': 'Usuaris',
|
||||
'Value already in database or empty': 'El valor ya existeix en la base de dades o està buit',
|
||||
'value already in database or empty': 'el valor ya existeix en la base de dades o està buit',
|
||||
'value not allowed': 'valor no permès',
|
||||
'Value not in database': 'El valor no està a la base de dades',
|
||||
'value not in database': 'el valor no està a la base de dades',
|
||||
'Verify Password': 'Verificar Contrasenya',
|
||||
'Version': 'Versió',
|
||||
'versioning': 'versions',
|
||||
'Videos': 'Videos',
|
||||
'View': 'Vista',
|
||||
'view': 'vista',
|
||||
'View %(entity)s': 'Veure %(entity)s',
|
||||
'View Page': 'View Page',
|
||||
'Views': 'Vistes',
|
||||
'views': 'vistes',
|
||||
'web2py is up to date': 'web2py està actualitzat',
|
||||
'web2py Recent Tweets': 'Tweets Recents de web2py',
|
||||
'Welcome': 'Benvingut',
|
||||
'Welcome %s': 'Benvingut %s',
|
||||
'Welcome to web2py': 'Benvingut a web2py',
|
||||
'Welcome to web2py!': '¡Benvingut a web2py!',
|
||||
'Which called the function %s located in the file %s': 'La qual va cridar la funció %s localitzada en el fitxer %s',
|
||||
'Wiki Page': 'Wiki Page',
|
||||
'Working...': 'Treballant ...',
|
||||
'XML': 'XML',
|
||||
'XML export of columns shown': 'XML export of columns shown',
|
||||
'YES': 'SÍ',
|
||||
'You are successfully running web2py': 'Vostè està executant web2py amb èxit',
|
||||
'You can modify this application and adapt it to your needs': 'Vostè pot modificar aquesta aplicació i adaptar-la a les seves necessitats',
|
||||
'You visited the url %s': 'Vostè va visitar la url %s',
|
||||
'Your username is: %(username)s': 'El seu nom de usuari és: %(username)s',
|
||||
}
|
||||
@@ -25,9 +25,9 @@ import sys
|
||||
import re
|
||||
import zipfile
|
||||
|
||||
#read web2py version from VERSION file
|
||||
#read web2py version from VERSION file
|
||||
web2py_version_line = readlines_file('VERSION')[0]
|
||||
#use regular expression to get just the version number
|
||||
#use regular expression to get just the version number
|
||||
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
|
||||
web2py_version = v_re.search(web2py_version_line).group(0)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
#Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py
|
||||
|
||||
|
||||
USAGE = """
|
||||
Usage:
|
||||
Copy this and setup_exe.conf to web2py root folder
|
||||
@@ -13,7 +13,7 @@ Usage:
|
||||
Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/
|
||||
run python setup_exe.py bbfreeze
|
||||
"""
|
||||
|
||||
|
||||
from distutils.core import setup
|
||||
from gluon.import_all import base_modules, contributed_modules
|
||||
from gluon.fileutils import readlines_file
|
||||
@@ -24,7 +24,7 @@ import shutil
|
||||
import sys
|
||||
import re
|
||||
import zipfile
|
||||
|
||||
|
||||
if len(sys.argv) != 2 or not os.path.isfile('web2py.py'):
|
||||
print USAGE
|
||||
sys.exit(1)
|
||||
@@ -32,11 +32,11 @@ BUILD_MODE = sys.argv[1]
|
||||
if not BUILD_MODE in ('py2exe', 'bbfreeze'):
|
||||
print USAGE
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def unzip(source_filename, dest_dir):
|
||||
with zipfile.ZipFile(source_filename) as zf:
|
||||
zf.extractall(dest_dir)
|
||||
|
||||
|
||||
#borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
|
||||
def recursive_zip(zipf, directory, folder=""):
|
||||
for item in os.listdir(directory):
|
||||
@@ -45,14 +45,14 @@ def recursive_zip(zipf, directory, folder=""):
|
||||
elif os.path.isdir(os.path.join(directory, item)):
|
||||
recursive_zip(
|
||||
zipf, os.path.join(directory, item), folder + os.sep + item)
|
||||
|
||||
|
||||
|
||||
|
||||
#read web2py version from VERSION file
|
||||
web2py_version_line = readlines_file('VERSION')[0]
|
||||
#use regular expression to get just the version number
|
||||
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
|
||||
web2py_version = v_re.search(web2py_version_line).group(0)
|
||||
|
||||
|
||||
#pull in preferences from config file
|
||||
import ConfigParser
|
||||
Config = ConfigParser.ConfigParser()
|
||||
@@ -68,12 +68,12 @@ include_gevent = Config.getboolean("Setup", "include_gevent")
|
||||
|
||||
# Python base version
|
||||
python_version = sys.version_info[:3]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if BUILD_MODE == 'py2exe':
|
||||
import py2exe
|
||||
|
||||
|
||||
setup(
|
||||
console=[{'script':'web2py.py',
|
||||
'icon_resources': [(0, 'extras/icons/web2py.ico')]
|
||||
@@ -88,8 +88,8 @@ if BUILD_MODE == 'py2exe':
|
||||
author="Massimo DiPierro",
|
||||
license="LGPL v3",
|
||||
data_files=[
|
||||
'ABOUT',
|
||||
'LICENSE',
|
||||
'ABOUT',
|
||||
'LICENSE',
|
||||
'VERSION'
|
||||
],
|
||||
options={'py2exe': {
|
||||
@@ -108,7 +108,7 @@ if BUILD_MODE == 'py2exe':
|
||||
zipl.close()
|
||||
shutil.rmtree(library_temp_dir)
|
||||
print "web2py binary successfully built"
|
||||
|
||||
|
||||
elif BUILD_MODE == 'bbfreeze':
|
||||
modules = base_modules + contributed_modules
|
||||
from bbfreeze import Freezer
|
||||
@@ -131,26 +131,26 @@ elif BUILD_MODE == 'bbfreeze':
|
||||
for req in ['ABOUT', 'LICENSE', 'VERSION']:
|
||||
shutil.copy(req, os.path.join('dist', req))
|
||||
print "web2py binary successfully built"
|
||||
|
||||
|
||||
try:
|
||||
os.unlink('storage.sqlite')
|
||||
except:
|
||||
pass
|
||||
|
||||
#This need to happen after bbfreeze is run because Freezer() deletes distdir before starting!
|
||||
|
||||
#This need to happen after bbfreeze is run because Freezer() deletes distdir before starting!
|
||||
if python_version > (2,5):
|
||||
# Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52
|
||||
try:
|
||||
shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/Microsoft.VC90.CRT/')
|
||||
except:
|
||||
print "You MUST copy Microsoft.VC90.CRT folder into the archive"
|
||||
|
||||
|
||||
def copy_folders(source, destination):
|
||||
"""Copy files & folders from source to destination (within dist/)"""
|
||||
if os.path.exists(os.path.join('dist', destination)):
|
||||
shutil.rmtree(os.path.join('dist', destination))
|
||||
shutil.copytree(os.path.join(source), os.path.join('dist', destination))
|
||||
|
||||
|
||||
#should we remove Windows OS dlls user is unlikely to be able to distribute
|
||||
if remove_msft_dlls:
|
||||
print "Deleted Microsoft files not licensed for open source distribution"
|
||||
@@ -166,7 +166,7 @@ if remove_msft_dlls:
|
||||
os.unlink(os.path.join('dist', f))
|
||||
except:
|
||||
print "unable to delete dist/" + f
|
||||
|
||||
|
||||
#Should we include applications?
|
||||
if copy_apps:
|
||||
copy_folders('applications', 'applications')
|
||||
@@ -177,12 +177,12 @@ else:
|
||||
copy_folders('applications/welcome', 'applications/welcome')
|
||||
copy_folders('applications/examples', 'applications/examples')
|
||||
print "Only web2py's admin, examples & welcome applications have been added"
|
||||
|
||||
|
||||
copy_folders('extras', 'extras')
|
||||
copy_folders('examples', 'examples')
|
||||
copy_folders('handlers', 'handlers')
|
||||
|
||||
|
||||
|
||||
|
||||
#should we copy project's site-packages into dist/site-packages
|
||||
if copy_site_packages:
|
||||
#copy site-packages
|
||||
@@ -190,7 +190,7 @@ if copy_site_packages:
|
||||
else:
|
||||
#no worries, web2py will create the (empty) folder first run
|
||||
print "Skipping site-packages"
|
||||
|
||||
|
||||
#should we copy project's scripts into dist/scripts
|
||||
if copy_scripts:
|
||||
#copy scripts
|
||||
@@ -198,7 +198,7 @@ if copy_scripts:
|
||||
else:
|
||||
#no worries, web2py will create the (empty) folder first run
|
||||
print "Skipping scripts"
|
||||
|
||||
|
||||
#should we create a zip file of the build?
|
||||
if make_zip:
|
||||
#create a web2py folder & copy dist's files into it
|
||||
@@ -209,13 +209,13 @@ if make_zip:
|
||||
# just temp so the web2py directory is included in our zip file
|
||||
path = 'zip_temp'
|
||||
# leave the first folder as None, as path is root.
|
||||
recursive_zip(zipf, path)
|
||||
recursive_zip(zipf, path)
|
||||
zipf.close()
|
||||
shutil.rmtree('zip_temp')
|
||||
print "Your Windows binary version of web2py can be found in " + \
|
||||
zip_filename + ".zip"
|
||||
print "You may extract the archive anywhere and then run web2py/web2py.exe"
|
||||
|
||||
|
||||
#should py2exe build files be removed?
|
||||
if remove_build_files:
|
||||
if BUILD_MODE == 'py2exe':
|
||||
@@ -223,10 +223,10 @@ if remove_build_files:
|
||||
shutil.rmtree('deposit')
|
||||
shutil.rmtree('dist')
|
||||
print "build files removed"
|
||||
|
||||
|
||||
#final info
|
||||
if not make_zip and not remove_build_files:
|
||||
print "Your Windows binary & associated files can also be found in /dist"
|
||||
|
||||
|
||||
print "Finished!"
|
||||
print "Enjoy web2py " + web2py_version_line
|
||||
print "Enjoy web2py " + web2py_version_line
|
||||
|
||||
+18
-1
@@ -11,7 +11,24 @@ Web2Py framework modules
|
||||
"""
|
||||
|
||||
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
|
||||
|
||||
|
||||
#: add pydal to sys.modules
|
||||
import os
|
||||
import sys
|
||||
try:
|
||||
sys.path.append(os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), "packages", "dal"))
|
||||
import pydal
|
||||
sys.modules['pydal'] = pydal
|
||||
except:
|
||||
raise RuntimeError(
|
||||
"web2py depends on pydal, which apparently you have not installed.\n" +
|
||||
"Probably you cloned the repository using git without '--recursive'.\n" +
|
||||
"To fix this, please run (from inside your web2py folder):\n\n" +
|
||||
" git submodule update\n\n" +
|
||||
"You can also download a complete copy from from http://www.web2py.com."
|
||||
)
|
||||
|
||||
from globals import current
|
||||
from html import *
|
||||
from validators import *
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ def app_cleanup(app, request):
|
||||
if os.path.exists(path):
|
||||
for f in os.listdir(path):
|
||||
try:
|
||||
if f[:1] != '.': os.unlink(os.path.join(path, f))
|
||||
if f[:1] != '.': recursive_unlink(os.path.join(path, f))
|
||||
except IOError:
|
||||
r = False
|
||||
return r
|
||||
|
||||
+704
-662
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -26,7 +26,7 @@ from gluon.fileutils import mktree, listdir, read_file, write_file
|
||||
from gluon.myregex import regex_expose, regex_longcomments
|
||||
from gluon.languages import translator
|
||||
from gluon.dal import DAL, Field
|
||||
from gluon.dal.base import BaseAdapter
|
||||
from pydal.base import BaseAdapter
|
||||
from gluon.sqlhtml import SQLFORM, SQLTABLE
|
||||
from gluon.cache import Cache
|
||||
from gluon.globals import current, Response
|
||||
@@ -127,7 +127,7 @@ class mybuiltin(object):
|
||||
def LOAD(c=None, f='index', args=None, vars=None,
|
||||
extension=None, target=None, ajax=False, ajax_trap=False,
|
||||
url=None, user_signature=False, timeout=None, times=1,
|
||||
content='loading...', **attr):
|
||||
content='loading...', post_vars=Storage(), **attr):
|
||||
""" LOADs a component into the action's document
|
||||
|
||||
Args:
|
||||
@@ -202,7 +202,7 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
||||
other_request.args = List(args)
|
||||
other_request.vars = vars
|
||||
other_request.get_vars = vars
|
||||
other_request.post_vars = Storage()
|
||||
other_request.post_vars = post_vars
|
||||
other_response = Response()
|
||||
other_request.env.path_info = '/' + \
|
||||
'/'.join([request.application, c, f] +
|
||||
@@ -407,7 +407,7 @@ def build_environment(request, response, session, store_current=True):
|
||||
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
|
||||
# /[controller]/[function]/*.py)
|
||||
response.models_to_run = [
|
||||
r'^\w+\.py$',
|
||||
r'^\w+\.py$',
|
||||
r'^%s/\w+\.py$' % request.controller,
|
||||
r'^%s/%s/\w+\.py$' % (request.controller, request.function)
|
||||
]
|
||||
@@ -514,7 +514,7 @@ def compile_controllers(folder):
|
||||
for function in exposed:
|
||||
command = data + "\nresponse._vars=response._caller(%s)\n" % \
|
||||
function
|
||||
filename = pjoin(folder, 'compiled',
|
||||
filename = pjoin(folder, 'compiled',
|
||||
'controllers.%s.%s.py' % (fname[:-3],function))
|
||||
write_file(filename, command)
|
||||
save_pyc(filename)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
|
||||
+10
-11
@@ -47,7 +47,7 @@ class Collection(object):
|
||||
if self.compact:
|
||||
for fieldname in (self.table_policy.get('fields',table.fields)):
|
||||
field = table[fieldname]
|
||||
if not ((field.type=='text' and text==False) or
|
||||
if not ((field.type=='text' and text==False) or
|
||||
field.type=='blob' or
|
||||
field.type.startswith('reference ') or
|
||||
field.type.startswith('list:reference ')) and field.name in row:
|
||||
@@ -56,7 +56,7 @@ class Collection(object):
|
||||
for fieldname in (self.table_policy.get('fields',table.fields)):
|
||||
field = table[fieldname]
|
||||
if not ((field.type=='text' and text==False) or
|
||||
field.type=='blob' or
|
||||
field.type=='blob' or
|
||||
field.type.startswith('reference ') or
|
||||
field.type.startswith('list:reference ')) and field.name in row:
|
||||
data.append({'name':field.name,'value':row[field.name],
|
||||
@@ -128,10 +128,10 @@ class Collection(object):
|
||||
for key,value in vars.items():
|
||||
if key=='_offset':
|
||||
limitby[0] = int(value) # MAY FAIL
|
||||
elif key == '_limit':
|
||||
elif key == '_limit':
|
||||
limitby[1] = int(value)+1 # MAY FAIL
|
||||
elif key=='_orderby':
|
||||
orderby = value
|
||||
orderby = value
|
||||
elif key in fieldnames:
|
||||
queries.append(table[key] == value)
|
||||
elif key.endswith('.eq') and key[:-3] in fieldnames: # for completeness (useless)
|
||||
@@ -156,14 +156,14 @@ class Collection(object):
|
||||
if filter_query:
|
||||
queries.append(filter_query)
|
||||
query = reduce(lambda a,b:a&b,queries[1:]) if len(queries)>1 else queries[0]
|
||||
orderby = [table[f] if f[0]!='~' else ~table[f[1:]] for f in orderby.split(',')]
|
||||
orderby = [table[f] if f[0]!='~' else ~table[f[1:]] for f in orderby.split(',')]
|
||||
return (query, limitby, orderby)
|
||||
|
||||
def table2queries(self,table, href):
|
||||
""" generates a set of collection.queries examples for the table """
|
||||
data = []
|
||||
for fieldname in (self.table_policy.get('fields', table.fields)):
|
||||
data.append({'name':fieldname,'value':''})
|
||||
data.append({'name':fieldname,'value':''})
|
||||
if self.extensions:
|
||||
data.append({'name':fieldname+'.ne','value':''}) # NEW !!!
|
||||
data.append({'name':fieldname+'.lt','value':''})
|
||||
@@ -192,7 +192,7 @@ class Collection(object):
|
||||
if not tablename:
|
||||
r['href'] = URL(scheme=True),
|
||||
# https://github.com/collection-json/extensions/blob/master/model.md
|
||||
r['links'] = [{'rel' : t, 'href' : URL(args=t,scheme=True), 'model':t}
|
||||
r['links'] = [{'rel' : t, 'href' : URL(args=t,scheme=True), 'model':t}
|
||||
for t in tablenames]
|
||||
response.headers['Content-Type'] = 'application/vnd.collection+json'
|
||||
return response.json({'collection':r})
|
||||
@@ -207,7 +207,7 @@ class Collection(object):
|
||||
# process GET
|
||||
if request.env.request_method=='GET':
|
||||
table = db[tablename]
|
||||
r['href'] = URL(args=tablename)
|
||||
r['href'] = URL(args=tablename)
|
||||
r['items'] = items = []
|
||||
try:
|
||||
(query, limitby, orderby) = self.request2query(table,request.get_vars)
|
||||
@@ -258,7 +258,7 @@ class Collection(object):
|
||||
return response.json({'collection':r})
|
||||
# process DELETE
|
||||
elif request.env.request_method=='DELETE':
|
||||
table = db[tablename]
|
||||
table = db[tablename]
|
||||
if not request.get_vars:
|
||||
return self.error(400, "BAD REQUEST", "Nothing to delete")
|
||||
else:
|
||||
@@ -312,7 +312,7 @@ class Collection(object):
|
||||
request, response = self.request, self.response
|
||||
r = OrderedDict({
|
||||
"version" : self.VERSION,
|
||||
"href" : URL(args=request.args,vars=request.vars),
|
||||
"href" : URL(args=request.args,vars=request.vars),
|
||||
"error" : {
|
||||
"title" : title,
|
||||
"code" : code,
|
||||
@@ -340,4 +340,3 @@ example_policies = {
|
||||
'DELETE':{'query':None},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class BrowserID(object):
|
||||
auth_info_json = fetch(self.verify_url, data=verify_data)
|
||||
j = json.loads(auth_info_json)
|
||||
epoch_time = int(time.time() * 1000) # we need 13 digit epoch time
|
||||
if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time:
|
||||
if j["status"] == "okay" and j["audience"] == audience and j['issuer'].endswith(issuer) and j['expires'] >= epoch_time:
|
||||
return dict(email=j['email'])
|
||||
elif self.on_login_failure:
|
||||
#print "status: ", j["status"]=="okay", j["status"]
|
||||
|
||||
@@ -254,12 +254,12 @@ class Table(DALStorage):
|
||||
self._db(self.id > 0).delete()
|
||||
|
||||
|
||||
def insert(self, **fields):
|
||||
# Checks 3 times that the id is new. 3 times is enough!
|
||||
for i in range(3):
|
||||
id = self._create_id()
|
||||
if self.get(id) is None and self.update(id, **fields):
|
||||
return long(id)
|
||||
def insert(self, **fields):
|
||||
# Checks 3 times that the id is new. 3 times is enough!
|
||||
for i in range(3):
|
||||
id = self._create_id()
|
||||
if self.get(id) is None and self.update(id, **fields):
|
||||
return long(id)
|
||||
else:
|
||||
raise RuntimeError("Too many ID conflicts")
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class Connection(object):
|
||||
parts = "complete"
|
||||
|
||||
return ("OK", (("%s " % message_id, message[parts]), message["flags"]))
|
||||
|
||||
|
||||
def _get_messages(self, query):
|
||||
if query.strip().isdigit():
|
||||
return [self.spam[self._mailbox][int(query.strip()) - 1],]
|
||||
@@ -151,7 +151,7 @@ class Connection(object):
|
||||
for item in self.spam[self._mailbox]:
|
||||
if item["uid"] == query[1:-1].replace("UID", "").strip():
|
||||
return [item,]
|
||||
messages = []
|
||||
messages = []
|
||||
try:
|
||||
for m in self.results[self._mailbox][query]:
|
||||
try:
|
||||
@@ -169,7 +169,7 @@ class Connection(object):
|
||||
return messages
|
||||
except KeyError:
|
||||
raise ValueError("The client issued an unexpected query: %s" % query)
|
||||
|
||||
|
||||
def setup(self, spam={}, results={}):
|
||||
"""adds custom message and query databases or sets
|
||||
the values to the module defaults.
|
||||
@@ -252,4 +252,3 @@ class IMAP4(object):
|
||||
return Connection()
|
||||
|
||||
IMAP4_SSL = IMAP4
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Note: This module is intended as a plugin replacement of pbkdf2.py
|
||||
by Armin Ronacher.
|
||||
|
||||
Git repository:
|
||||
Git repository:
|
||||
$ git clone https://github.com/michele-comitini/pbkdf2_ctypes.git
|
||||
|
||||
:copyright: Copyright (c) 2013: Michele Comitini <mcm@glisco.it>
|
||||
@@ -86,7 +86,7 @@ def _openssl_hashlib_to_crypto_map_get(hashfunc):
|
||||
crypto_hashfunc.restype = ctypes.c_void_p
|
||||
return crypto_hashfunc()
|
||||
|
||||
|
||||
|
||||
def _openssl_pbkdf2(data, salt, iterations, digest, keylen):
|
||||
"""OpenSSL compatibile wrapper
|
||||
"""
|
||||
@@ -99,7 +99,7 @@ def _openssl_pbkdf2(data, salt, iterations, digest, keylen):
|
||||
c_iter = ctypes.c_int(iterations)
|
||||
c_keylen = ctypes.c_int(keylen)
|
||||
c_buff = ctypes.create_string_buffer(keylen)
|
||||
|
||||
|
||||
# PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
|
||||
# const unsigned char *salt, int saltlen, int iter,
|
||||
# const EVP_MD *digest,
|
||||
@@ -109,7 +109,7 @@ def _openssl_pbkdf2(data, salt, iterations, digest, keylen):
|
||||
ctypes.c_char_p, ctypes.c_int,
|
||||
ctypes.c_int, ctypes.c_void_p,
|
||||
ctypes.c_int, ctypes.c_char_p]
|
||||
|
||||
|
||||
crypto.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int
|
||||
err = crypto.PKCS5_PBKDF2_HMAC(c_pass, c_passlen,
|
||||
c_salt, c_saltlen,
|
||||
|
||||
+408
-408
File diff suppressed because it is too large
Load Diff
+22
-22
@@ -37,7 +37,7 @@ def pay():
|
||||
elif form.errors:
|
||||
redirect(URL('pay_error'))
|
||||
return dict(form=form)
|
||||
|
||||
|
||||
"""
|
||||
|
||||
URL_CHARGE = 'https://%s:@api.stripe.com/v1/charges'
|
||||
@@ -114,7 +114,7 @@ class StripeForm(object):
|
||||
|
||||
def process(self):
|
||||
from gluon import current
|
||||
request = current.request
|
||||
request = current.request
|
||||
if request.post_vars:
|
||||
if self.signature == request.post_vars.signature:
|
||||
self.response = Stripe(self.sk).charge(
|
||||
@@ -127,7 +127,7 @@ class StripeForm(object):
|
||||
return self
|
||||
self.errors = True
|
||||
return self
|
||||
|
||||
|
||||
def xml(self):
|
||||
from gluon.template import render
|
||||
if self.accepted:
|
||||
@@ -135,8 +135,8 @@ class StripeForm(object):
|
||||
elif self.errors:
|
||||
return "There was an processing error"
|
||||
else:
|
||||
context = dict(amount=self.amount,
|
||||
signature=self.signature, pk=self.pk,
|
||||
context = dict(amount=self.amount,
|
||||
signature=self.signature, pk=self.pk,
|
||||
currency_symbol=self.currency_symbol,
|
||||
security_notice=self.security_notice,
|
||||
disclosure_notice=self.disclosure_notice)
|
||||
@@ -145,14 +145,14 @@ class StripeForm(object):
|
||||
|
||||
TEMPLATE = """
|
||||
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
||||
<script>
|
||||
<script>
|
||||
jQuery(function(){
|
||||
// This identifies your website in the createToken call below
|
||||
Stripe.setPublishableKey('{{=pk}}');
|
||||
|
||||
|
||||
var stripeResponseHandler = function(status, response) {
|
||||
var jQueryform = jQuery('#payment-form');
|
||||
|
||||
|
||||
if (response.error) {
|
||||
// Show the errors on the form
|
||||
jQuery('.payment-errors').text(response.error.message).show();
|
||||
@@ -167,17 +167,17 @@ jQuery(function(){
|
||||
jQueryform.get(0).submit();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
jQuery(function(jQuery) {
|
||||
jQuery('#payment-form').submit(function(e) {
|
||||
|
||||
var jQueryform = jQuery(this);
|
||||
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
jQueryform.find('button').prop('disabled', true);
|
||||
|
||||
|
||||
Stripe.createToken(jQueryform, stripeResponseHandler);
|
||||
|
||||
|
||||
// Prevent the form from submitting with the default action
|
||||
return false;
|
||||
});
|
||||
@@ -189,33 +189,33 @@ jQuery(function(){
|
||||
<form action="" method="POST" id="payment-form" class="form-horizontal">
|
||||
|
||||
<div class="form-row control-group">
|
||||
<label class="control-label">Card Number</label>
|
||||
<label class="control-label">Card Number</label>
|
||||
<div class="controls">
|
||||
<input type="text" size="20" data-stripe="number"
|
||||
placeholder="4242424242424242"/>
|
||||
placeholder="4242424242424242"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row control-group">
|
||||
<label class="control-label">CVC</label>
|
||||
<label class="control-label">CVC</label>
|
||||
<div class="controls">
|
||||
<input type="text" size="4" style="width:80px" data-stripe="cvc"
|
||||
placeholder="XXX"/>
|
||||
placeholder="XXX"/>
|
||||
<a href="http://en.wikipedia.org/wiki/Card_Verification_Code" target="_blank">What is this?</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row control-group">
|
||||
<label class="control-label">Expiration</label>
|
||||
<label class="control-label">Expiration</label>
|
||||
<div class="controls">
|
||||
<input type="text" size="2" style="width:40px" data-stripe="exp-month"
|
||||
placeholder="MM"/>
|
||||
placeholder="MM"/>
|
||||
/
|
||||
<input type="text" size="4" style="width:80px" data-stripe="exp-year"
|
||||
placeholder="YYYY"/>
|
||||
placeholder="YYYY"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
"""
|
||||
Developed by Massimo Di Pierro
|
||||
Released under the web2py license (LGPL)
|
||||
|
||||
@@ -74,9 +74,9 @@ https is possible too using 'https://127.0.0.1:8888' instead of 'http://127.0.0.
|
||||
be started with
|
||||
|
||||
python gluon/contrib/websocket_messaging.py -k mykey -p 8888 -s keyfile.pem -c certfile.pem
|
||||
|
||||
|
||||
for secure websocket do:
|
||||
|
||||
|
||||
web2py_websocket('wss://127.0.0.1:8888/realtime/mygroup',callback)
|
||||
|
||||
Acknowledgements:
|
||||
|
||||
@@ -41,7 +41,7 @@ class CustomImportException(ImportError):
|
||||
|
||||
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
||||
"""
|
||||
web2py's custom importer. It behaves like the standard Python importer but
|
||||
web2py's custom importer. It behaves like the standard Python importer but
|
||||
it tries to transform import statements as something like
|
||||
"import applications.app_name.modules.x".
|
||||
If the import fails, it falls back on naive_importer
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pydal import DAL as pyDAL
|
||||
from pydal import Field, SQLCustomType, geoPoint, geoLine, geoPolygon
|
||||
|
||||
|
||||
def _default_validators(db, field):
|
||||
"""
|
||||
Field type validation, using web2py's validators mechanism.
|
||||
|
||||
makes sure the content of a field is in line with the declared
|
||||
fieldtype
|
||||
"""
|
||||
from gluon import validators
|
||||
field_type, field_length = field.type, field.length
|
||||
requires = []
|
||||
|
||||
def ff(r, id):
|
||||
row = r(id)
|
||||
if not row:
|
||||
return str(id)
|
||||
elif hasattr(r, '_format') and isinstance(r._format, str):
|
||||
return r._format % row
|
||||
elif hasattr(r, '_format') and callable(r._format):
|
||||
return r._format(row)
|
||||
else:
|
||||
return str(id)
|
||||
|
||||
if field_type in (('string', 'text', 'password')):
|
||||
requires.append(validators.IS_LENGTH(field_length))
|
||||
elif field_type == 'json':
|
||||
requires.append(validators.IS_EMPTY_OR(validators.IS_JSON()))
|
||||
elif field_type == 'double' or field_type == 'float':
|
||||
requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100))
|
||||
elif field_type == 'integer':
|
||||
requires.append(validators.IS_INT_IN_RANGE(-2**31, 2**31))
|
||||
elif field_type == 'bigint':
|
||||
requires.append(validators.IS_INT_IN_RANGE(-2**63, 2**63))
|
||||
elif field_type.startswith('decimal'):
|
||||
requires.append(validators.IS_DECIMAL_IN_RANGE(-10**10, 10**10))
|
||||
elif field_type == 'date':
|
||||
requires.append(validators.IS_DATE())
|
||||
elif field_type == 'time':
|
||||
requires.append(validators.IS_TIME())
|
||||
elif field_type == 'datetime':
|
||||
requires.append(validators.IS_DATETIME())
|
||||
elif db and field_type.startswith('reference') and \
|
||||
field_type.find('.') < 0 and \
|
||||
field_type[10:] in db.tables:
|
||||
referenced = db[field_type[10:]]
|
||||
|
||||
def repr_ref(id, row=None, r=referenced, f=ff):
|
||||
return f(r, id)
|
||||
|
||||
field.represent = field.represent or repr_ref
|
||||
if hasattr(referenced, '_format') and referenced._format:
|
||||
requires = validators.IS_IN_DB(db, referenced._id,
|
||||
referenced._format)
|
||||
if field.unique:
|
||||
requires._and = validators.IS_NOT_IN_DB(db, field)
|
||||
if field.tablename == field_type[10:]:
|
||||
return validators.IS_EMPTY_OR(requires)
|
||||
return requires
|
||||
elif db and field_type.startswith('list:reference') and \
|
||||
field_type.find('.') < 0 and \
|
||||
field_type[15:] in db.tables:
|
||||
referenced = db[field_type[15:]]
|
||||
|
||||
def list_ref_repr(ids, row=None, r=referenced, f=ff):
|
||||
if not ids:
|
||||
return None
|
||||
from pydal.adapters import GoogleDatastoreAdapter
|
||||
refs = None
|
||||
db, id = r._db, r._id
|
||||
if isinstance(db._adapter, GoogleDatastoreAdapter):
|
||||
def count(values):
|
||||
return db(id.belongs(values)).select(id)
|
||||
rx = range(0, len(ids), 30)
|
||||
refs = reduce(lambda a, b: a & b, [count(ids[i:i+30])
|
||||
for i in rx])
|
||||
else:
|
||||
refs = db(id.belongs(ids)).select(id)
|
||||
return (refs and ', '.join(f(r, x.id) for x in refs) or '')
|
||||
|
||||
field.represent = field.represent or list_ref_repr
|
||||
if hasattr(referenced, '_format') and referenced._format:
|
||||
requires = validators.IS_IN_DB(db, referenced._id,
|
||||
referenced._format, multiple=True)
|
||||
else:
|
||||
requires = validators.IS_IN_DB(db, referenced._id,
|
||||
multiple=True)
|
||||
if field.unique:
|
||||
requires._and = validators.IS_NOT_IN_DB(db, field)
|
||||
if not field.notnull:
|
||||
requires = validators.IS_EMPTY_OR(requires)
|
||||
return requires
|
||||
elif field_type.startswith('list:'):
|
||||
def repr_list(values, row=None):
|
||||
return', '.join(str(v) for v in (values or []))
|
||||
|
||||
field.represent = field.represent or repr_list
|
||||
|
||||
if field.unique:
|
||||
requires.append(validators.IS_NOT_IN_DB(db, field))
|
||||
sff = ['in', 'do', 'da', 'ti', 'de', 'bo']
|
||||
if field.notnull and not field_type[:2] in sff:
|
||||
requires.append(validators.IS_NOT_EMPTY())
|
||||
elif not field.notnull and field_type[:2] in sff and requires:
|
||||
requires[0] = validators.IS_EMPTY_OR(requires[0])
|
||||
return requires
|
||||
|
||||
from gluon import serializers as w2p_serializers
|
||||
from gluon.utils import web2py_uuid
|
||||
from gluon import sqlhtml
|
||||
|
||||
|
||||
class DAL(pyDAL):
|
||||
serializers = w2p_serializers
|
||||
validators_method = _default_validators
|
||||
uuid = lambda x: web2py_uuid()
|
||||
representers = {
|
||||
'rows_render': sqlhtml.represent,
|
||||
'rows_xml': sqlhtml.SQLTABLE
|
||||
}
|
||||
|
||||
#: add web2py contrib drivers to pyDAL
|
||||
from pydal.drivers import DRIVERS
|
||||
if not DRIVERS.get('pymysql'):
|
||||
from .contrib import pymysql
|
||||
DRIVERS['pymysql'] = pymysql
|
||||
#if not DRIVERS.get('pg8000'):
|
||||
# from .contrib import pg8000
|
||||
# DRIVERS['pg8000'] = pg8000
|
||||
if not DRIVERS.get('pyodbc'):
|
||||
from .contrib import pypyodbc as pyodbc
|
||||
DRIVERS['pyodbc'] = pyodbc
|
||||
@@ -1,4 +0,0 @@
|
||||
from .base import DAL
|
||||
from .objects import Field
|
||||
from .helpers.classes import SQLCustomType
|
||||
from .helpers.methods import geoPoint, geoLine, geoPolygon
|
||||
@@ -1,21 +0,0 @@
|
||||
import sys
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
|
||||
if PY2:
|
||||
import cPickle as pickle
|
||||
import cStringIO as StringIO
|
||||
import copy_reg as copyreg
|
||||
hashlib_md5 = hashlib.md5
|
||||
else:
|
||||
import pickle
|
||||
from io import StringIO
|
||||
import copyreg
|
||||
hashlib_md5 = lambda s: hashlib.md5(bytes(s,'utf8'))
|
||||
|
||||
pjoin = os.path.join
|
||||
exists = os.path.exists
|
||||
ogetattr = object.__getattribute__
|
||||
osetattr = object.__setattr__
|
||||
@@ -1,13 +0,0 @@
|
||||
import threading
|
||||
import logging
|
||||
|
||||
GLOBAL_LOCKER = threading.RLock()
|
||||
THREAD_LOCAL = threading.local()
|
||||
|
||||
LOGGER = logging.getLogger("web2py.dal")
|
||||
|
||||
DEFAULT = lambda: None
|
||||
|
||||
def IDENTITY(x): return x
|
||||
def OR(a,b): return a|b
|
||||
def AND(a,b): return a&b
|
||||
@@ -1,313 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import decimal
|
||||
import re
|
||||
|
||||
from ._globals import LOGGER
|
||||
|
||||
|
||||
# verify presence of web2py modules
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except:
|
||||
from gluon.contrib.ordereddict import OrderedDict
|
||||
|
||||
try:
|
||||
from gluon.utils import web2py_uuid
|
||||
except (ImportError, SystemError):
|
||||
import uuid
|
||||
def web2py_uuid(): return str(uuid.uuid4())
|
||||
|
||||
try:
|
||||
import portalocker
|
||||
have_portalocker = True
|
||||
except ImportError:
|
||||
portalocker = None
|
||||
have_portalocker = False
|
||||
|
||||
try:
|
||||
from gluon import serializers
|
||||
have_serializers = True
|
||||
simplejson = None
|
||||
except ImportError:
|
||||
serializers = None
|
||||
have_serializers = False
|
||||
try:
|
||||
import json as simplejson
|
||||
except ImportError:
|
||||
try:
|
||||
import gluon.contrib.simplejson as simplejson
|
||||
except ImportError:
|
||||
simplejson = None
|
||||
|
||||
|
||||
# list of drivers will be built on the fly
|
||||
# and lists only what is available
|
||||
DRIVERS = []
|
||||
|
||||
try:
|
||||
from new import classobj
|
||||
from google.appengine.ext import db as gae
|
||||
from google.appengine.ext import ndb
|
||||
from google.appengine.api import namespace_manager, rdbms
|
||||
from google.appengine.api.datastore_types import Key ### for belongs on ID
|
||||
from google.appengine.ext.db.polymodel import PolyModel
|
||||
from google.appengine.ext.ndb.polymodel import PolyModel as NDBPolyModel
|
||||
DRIVERS.append('google')
|
||||
except ImportError:
|
||||
classobj = None
|
||||
gae = None
|
||||
ndb = None
|
||||
namespace_manager = rdbms = None
|
||||
Key = None
|
||||
PolyModel = NDBPolyModel = None
|
||||
|
||||
if not 'google' in DRIVERS:
|
||||
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite2
|
||||
DRIVERS.append('sqlite2')
|
||||
except ImportError:
|
||||
LOGGER.debug('no SQLite drivers pysqlite2.dbapi2')
|
||||
|
||||
try:
|
||||
from sqlite3 import dbapi2 as sqlite3
|
||||
DRIVERS.append('sqlite3')
|
||||
except ImportError:
|
||||
LOGGER.debug('no SQLite drivers sqlite3')
|
||||
|
||||
try:
|
||||
# first try contrib driver, then from site-packages (if installed)
|
||||
try:
|
||||
import gluon.contrib.pymysql as pymysql
|
||||
# monkeypatch pymysql because they havent fixed the bug:
|
||||
# https://github.com/petehunt/PyMySQL/issues/86
|
||||
pymysql.ESCAPE_REGEX = re.compile("'")
|
||||
pymysql.ESCAPE_MAP = {"'": "''"}
|
||||
# end monkeypatch
|
||||
except ImportError:
|
||||
import pymysql
|
||||
DRIVERS.append('pymysql')
|
||||
except ImportError:
|
||||
LOGGER.debug('no MySQL driver pymysql')
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
DRIVERS.append('MySQLdb')
|
||||
except ImportError:
|
||||
LOGGER.debug('no MySQL driver MySQLDB')
|
||||
|
||||
try:
|
||||
import mysql.connector as mysqlconnector
|
||||
DRIVERS.append("mysqlconnector")
|
||||
except ImportError:
|
||||
LOGGER.debug("no driver mysql.connector")
|
||||
|
||||
try:
|
||||
import psycopg2
|
||||
from psycopg2.extensions import adapt as psycopg2_adapt
|
||||
DRIVERS.append('psycopg2')
|
||||
except ImportError:
|
||||
psycopg2_adapt = None
|
||||
LOGGER.debug('no PostgreSQL driver psycopg2')
|
||||
|
||||
try:
|
||||
# first try contrib driver, then from site-packages (if installed)
|
||||
try:
|
||||
import gluon.contrib.pg8000.dbapi as pg8000
|
||||
except ImportError:
|
||||
import pg8000.dbapi as pg8000
|
||||
DRIVERS.append('pg8000')
|
||||
except ImportError:
|
||||
LOGGER.debug('no PostgreSQL driver pg8000')
|
||||
|
||||
try:
|
||||
import cx_Oracle
|
||||
DRIVERS.append('cx_Oracle')
|
||||
except ImportError:
|
||||
cx_Oracle = None
|
||||
LOGGER.debug('no Oracle driver cx_Oracle')
|
||||
|
||||
try:
|
||||
try:
|
||||
import pyodbc
|
||||
except ImportError:
|
||||
try:
|
||||
import gluon.contrib.pypyodbc as pyodbc
|
||||
except Exception, e:
|
||||
raise ImportError(str(e))
|
||||
DRIVERS.append('pyodbc')
|
||||
#DRIVERS.append('DB2(pyodbc)')
|
||||
#DRIVERS.append('Teradata(pyodbc)')
|
||||
#DRIVERS.append('Ingres(pyodbc)')
|
||||
except ImportError:
|
||||
pyodbc = None
|
||||
LOGGER.debug('no MSSQL/DB2/Teradata/Ingres driver pyodbc')
|
||||
|
||||
try:
|
||||
import ibm_db_dbi
|
||||
DRIVERS.append('ibm_db_dbi')
|
||||
except ImportError:
|
||||
LOGGER.debug('no DB2 driver ibm_db_dbi')
|
||||
|
||||
try:
|
||||
import Sybase
|
||||
DRIVERS.append('Sybase')
|
||||
except ImportError:
|
||||
LOGGER.debug('no Sybase driver')
|
||||
|
||||
try:
|
||||
import kinterbasdb
|
||||
DRIVERS.append('kinterbasdb')
|
||||
#DRIVERS.append('Firebird(kinterbasdb)')
|
||||
except ImportError:
|
||||
LOGGER.debug('no Firebird/Interbase driver kinterbasdb')
|
||||
|
||||
try:
|
||||
import fdb
|
||||
DRIVERS.append('fdb')
|
||||
except ImportError:
|
||||
LOGGER.debug('no Firebird driver fdb')
|
||||
|
||||
try:
|
||||
import firebirdsql
|
||||
DRIVERS.append('firebirdsql')
|
||||
except ImportError:
|
||||
LOGGER.debug('no Firebird driver firebirdsql')
|
||||
|
||||
try:
|
||||
import informixdb
|
||||
DRIVERS.append('informixdb')
|
||||
LOGGER.warning('Informix support is experimental')
|
||||
except ImportError:
|
||||
LOGGER.debug('no Informix driver informixdb')
|
||||
|
||||
try:
|
||||
import sapdb
|
||||
DRIVERS.append('sapdb')
|
||||
LOGGER.warning('SAPDB support is experimental')
|
||||
except ImportError:
|
||||
LOGGER.debug('no SAP driver sapdb')
|
||||
|
||||
try:
|
||||
import cubriddb
|
||||
DRIVERS.append('cubriddb')
|
||||
LOGGER.warning('Cubrid support is experimental')
|
||||
except ImportError:
|
||||
LOGGER.debug('no Cubrid driver cubriddb')
|
||||
|
||||
try:
|
||||
from com.ziclix.python.sql import zxJDBC
|
||||
import java.sql
|
||||
# Try sqlite jdbc driver from http://www.zentus.com/sqlitejdbc/
|
||||
from org.sqlite import JDBC # required by java.sql; ensure we have it
|
||||
zxJDBC_sqlite = java.sql.DriverManager
|
||||
DRIVERS.append('zxJDBC')
|
||||
#DRIVERS.append('SQLite(zxJDBC)')
|
||||
LOGGER.warning('zxJDBC support is experimental')
|
||||
is_jdbc = True
|
||||
except ImportError:
|
||||
LOGGER.debug('no SQLite/PostgreSQL driver zxJDBC')
|
||||
is_jdbc = False
|
||||
|
||||
try:
|
||||
import couchdb
|
||||
DRIVERS.append('couchdb')
|
||||
except ImportError:
|
||||
couchdb = None
|
||||
LOGGER.debug('no Couchdb driver couchdb')
|
||||
|
||||
try:
|
||||
import pymongo
|
||||
DRIVERS.append('pymongo')
|
||||
except:
|
||||
LOGGER.debug('no MongoDB driver pymongo')
|
||||
|
||||
try:
|
||||
import imaplib
|
||||
DRIVERS.append('imaplib')
|
||||
except:
|
||||
LOGGER.debug('no IMAP driver imaplib')
|
||||
|
||||
GAEDecimalProperty = None
|
||||
NDBDecimalProperty = None
|
||||
else:
|
||||
is_jdbc = False
|
||||
|
||||
class GAEDecimalProperty(gae.Property):
|
||||
"""
|
||||
GAE decimal implementation
|
||||
"""
|
||||
data_type = decimal.Decimal
|
||||
|
||||
def __init__(self, precision, scale, **kwargs):
|
||||
super(GAEDecimalProperty, self).__init__(self, **kwargs)
|
||||
d = '1.'
|
||||
for x in range(scale):
|
||||
d += '0'
|
||||
self.round = decimal.Decimal(d)
|
||||
|
||||
def get_value_for_datastore(self, model_instance):
|
||||
value = super(GAEDecimalProperty, self)\
|
||||
.get_value_for_datastore(model_instance)
|
||||
if value is None or value == '':
|
||||
return None
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
def make_value_from_datastore(self, value):
|
||||
if value is None or value == '':
|
||||
return None
|
||||
else:
|
||||
return decimal.Decimal(value).quantize(self.round)
|
||||
|
||||
def validate(self, value):
|
||||
value = super(GAEDecimalProperty, self).validate(value)
|
||||
if value is None or isinstance(value, decimal.Decimal):
|
||||
return value
|
||||
elif isinstance(value, basestring):
|
||||
return decimal.Decimal(value)
|
||||
raise gae.BadValueError("Property %s must be a Decimal or string."\
|
||||
% self.name)
|
||||
|
||||
#TODO Needs more testing
|
||||
class NDBDecimalProperty(ndb.StringProperty):
|
||||
"""
|
||||
NDB decimal implementation
|
||||
"""
|
||||
data_type = decimal.Decimal
|
||||
|
||||
def __init__(self, precision, scale, **kwargs):
|
||||
d = '1.'
|
||||
for x in range(scale):
|
||||
d += '0'
|
||||
self.round = decimal.Decimal(d)
|
||||
|
||||
def _to_base_type(self, value):
|
||||
if value is None or value == '':
|
||||
return None
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
def _from_base_type(self, value):
|
||||
if value is None or value == '':
|
||||
return None
|
||||
else:
|
||||
return decimal.Decimal(value).quantize(self.round)
|
||||
|
||||
def _validate(self, value):
|
||||
if value is None or isinstance(value, decimal.Decimal):
|
||||
return value
|
||||
elif isinstance(value, basestring):
|
||||
return decimal.Decimal(value)
|
||||
raise TypeError("Property %s must be a Decimal or string."\
|
||||
% self._name)
|
||||
|
||||
psycopg2_adapt = None
|
||||
cx_Oracle = None
|
||||
pyodbc = None
|
||||
couchdb = None
|
||||
|
||||
|
||||
def get_driver(name):
|
||||
return globals().get(name)
|
||||
@@ -1,60 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .sqlite import SQLiteAdapter, SpatiaLiteAdapter, JDBCSQLiteAdapter
|
||||
from .mysql import MySQLAdapter
|
||||
from .postgres import PostgreSQLAdapter, NewPostgreSQLAdapter, JDBCPostgreSQLAdapter
|
||||
from .oracle import OracleAdapter
|
||||
from .mssql import MSSQLAdapter, MSSQL2Adapter, MSSQL3Adapter, MSSQL4Adapter, \
|
||||
VerticaAdapter, SybaseAdapter
|
||||
from .firebird import FireBirdAdapter
|
||||
from .informix import InformixAdapter, InformixSEAdapter
|
||||
from .db2 import DB2Adapter
|
||||
from .teradata import TeradataAdapter
|
||||
from .ingres import IngresAdapter, IngresUnicodeAdapter
|
||||
from .sapdb import SAPDBAdapter
|
||||
from .cubrid import CubridAdapter
|
||||
from .google import GoogleDatastoreAdapter, GoogleSQLAdapter
|
||||
from .couchdb import CouchDBAdapter
|
||||
from .mongo import MongoDBAdapter
|
||||
from .imap import IMAPAdapter
|
||||
|
||||
|
||||
ADAPTERS = {
|
||||
'sqlite': SQLiteAdapter,
|
||||
'spatialite': SpatiaLiteAdapter,
|
||||
'sqlite:memory': SQLiteAdapter,
|
||||
'spatialite:memory': SpatiaLiteAdapter,
|
||||
'mysql': MySQLAdapter,
|
||||
'postgres': PostgreSQLAdapter,
|
||||
'postgres:psycopg2': PostgreSQLAdapter,
|
||||
'postgres:pg8000': PostgreSQLAdapter,
|
||||
'postgres2:psycopg2': NewPostgreSQLAdapter,
|
||||
'postgres2:pg8000': NewPostgreSQLAdapter,
|
||||
'oracle': OracleAdapter,
|
||||
'mssql': MSSQLAdapter,
|
||||
'mssql2': MSSQL2Adapter,
|
||||
'mssql3': MSSQL3Adapter,
|
||||
'mssql4' : MSSQL4Adapter,
|
||||
'vertica': VerticaAdapter,
|
||||
'sybase': SybaseAdapter,
|
||||
'db2:ibm_db_dbi': DB2Adapter,
|
||||
'db2:pyodbc': DB2Adapter,
|
||||
'teradata': TeradataAdapter,
|
||||
'informix': InformixAdapter,
|
||||
'informix-se': InformixSEAdapter,
|
||||
'firebird': FireBirdAdapter,
|
||||
'firebird_embedded': FireBirdAdapter,
|
||||
'ingres': IngresAdapter,
|
||||
'ingresu': IngresUnicodeAdapter,
|
||||
'sapdb': SAPDBAdapter,
|
||||
'cubrid': CubridAdapter,
|
||||
'jdbc:sqlite': JDBCSQLiteAdapter,
|
||||
'jdbc:sqlite:memory': JDBCSQLiteAdapter,
|
||||
'jdbc:postgres': JDBCPostgreSQLAdapter,
|
||||
'gae': GoogleDatastoreAdapter, # discouraged, for backward compatibility
|
||||
'google:datastore': GoogleDatastoreAdapter,
|
||||
'google:datastore+ndb': GoogleDatastoreAdapter,
|
||||
'google:sql': GoogleSQLAdapter,
|
||||
'couchdb': CouchDBAdapter,
|
||||
'mongodb': MongoDBAdapter,
|
||||
'imap': IMAPAdapter
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,202 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .._load import serializers, couchdb, web2py_uuid
|
||||
from ..objects import Field, Query
|
||||
from ..helpers.classes import SQLALL
|
||||
from ..helpers.methods import uuid2int
|
||||
from .base import BaseAdapter, NoSQLAdapter, SELECT_ARGS
|
||||
|
||||
|
||||
class CouchDBAdapter(NoSQLAdapter):
|
||||
drivers = ('couchdb',)
|
||||
|
||||
uploads_in_blob = True
|
||||
types = {
|
||||
'boolean': bool,
|
||||
'string': str,
|
||||
'text': str,
|
||||
'json': str,
|
||||
'password': str,
|
||||
'blob': str,
|
||||
'upload': str,
|
||||
'integer': long,
|
||||
'bigint': long,
|
||||
'float': float,
|
||||
'double': float,
|
||||
'date': datetime.date,
|
||||
'time': datetime.time,
|
||||
'datetime': datetime.datetime,
|
||||
'id': long,
|
||||
'reference': long,
|
||||
'list:string': list,
|
||||
'list:integer': list,
|
||||
'list:reference': list,
|
||||
}
|
||||
|
||||
def file_exists(self, filename): pass
|
||||
def file_open(self, filename, mode='rb', lock=True): pass
|
||||
def file_close(self, fileobj): pass
|
||||
|
||||
def expand(self,expression,field_type=None):
|
||||
if isinstance(expression,Field):
|
||||
if expression.type=='id':
|
||||
return "%s._id" % expression.tablename
|
||||
return BaseAdapter.expand(self,expression,field_type)
|
||||
|
||||
def AND(self,first,second):
|
||||
return '(%s && %s)' % (self.expand(first),self.expand(second))
|
||||
|
||||
def OR(self,first,second):
|
||||
return '(%s || %s)' % (self.expand(first),self.expand(second))
|
||||
|
||||
def EQ(self,first,second):
|
||||
if second is None:
|
||||
return '(%s == null)' % self.expand(first)
|
||||
return '(%s == %s)' % (self.expand(first),self.expand(second,first.type))
|
||||
|
||||
def NE(self,first,second):
|
||||
if second is None:
|
||||
return '(%s != null)' % self.expand(first)
|
||||
return '(%s != %s)' % (self.expand(first),self.expand(second,first.type))
|
||||
|
||||
def COMMA(self,first,second):
|
||||
return '%s + %s' % (self.expand(first),self.expand(second))
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
value = NoSQLAdapter.represent(self, obj, fieldtype)
|
||||
if fieldtype=='id':
|
||||
return repr(str(long(value)))
|
||||
elif fieldtype in ('date','time','datetime','boolean'):
|
||||
return serializers.json(value)
|
||||
return repr(not isinstance(value,unicode) and value \
|
||||
or value and value.encode('utf8'))
|
||||
|
||||
def __init__(self,db,uri='couchdb://127.0.0.1:5984',
|
||||
pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args)
|
||||
self.dbengine = 'couchdb'
|
||||
self.folder = folder
|
||||
db['_lastsql'] = ''
|
||||
self.db_codec = 'UTF-8'
|
||||
self._after_connection = after_connection
|
||||
self.pool_size = pool_size
|
||||
|
||||
url='http://'+uri[10:]
|
||||
def connector(url=url,driver_args=driver_args):
|
||||
return self.driver.Server(url,**driver_args)
|
||||
self.reconnect(connector,cursor=False)
|
||||
|
||||
def create_table(self, table, migrate=True, fake_migrate=False, polymodel=None):
|
||||
if migrate:
|
||||
try:
|
||||
self.connection.create(table._tablename)
|
||||
except:
|
||||
pass
|
||||
|
||||
def insert(self,table,fields):
|
||||
id = uuid2int(web2py_uuid())
|
||||
ctable = self.connection[table._tablename]
|
||||
values = dict((k.name,self.represent(v,k.type)) for k,v in fields)
|
||||
values['_id'] = str(id)
|
||||
ctable.save(values)
|
||||
return id
|
||||
|
||||
def _select(self,query,fields,attributes):
|
||||
if not isinstance(query,Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
for key in set(attributes.keys())-SELECT_ARGS:
|
||||
raise SyntaxError('invalid select attribute: %s' % key)
|
||||
new_fields=[]
|
||||
for item in fields:
|
||||
if isinstance(item,SQLALL):
|
||||
new_fields += item._table
|
||||
else:
|
||||
new_fields.append(item)
|
||||
def uid(fd):
|
||||
return fd=='id' and '_id' or fd
|
||||
def get(row,fd):
|
||||
return fd=='id' and long(row['_id']) or row.get(fd,None)
|
||||
fields = new_fields
|
||||
tablename = self.get_table(query)
|
||||
fieldnames = [f.name for f in (fields or self.db[tablename])]
|
||||
colnames = ['%s.%s' % (tablename,k) for k in fieldnames]
|
||||
fields = ','.join(['%s.%s' % (tablename,uid(f)) for f in fieldnames])
|
||||
fn="(function(%(t)s){if(%(query)s)emit(%(order)s,[%(fields)s]);})" %\
|
||||
dict(t=tablename,
|
||||
query=self.expand(query),
|
||||
order='%s._id' % tablename,
|
||||
fields=fields)
|
||||
return fn, colnames
|
||||
|
||||
def select(self,query,fields,attributes):
|
||||
if not isinstance(query,Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
fn, colnames = self._select(query,fields,attributes)
|
||||
tablename = colnames[0].split('.')[0]
|
||||
ctable = self.connection[tablename]
|
||||
rows = [cols['value'] for cols in ctable.query(fn)]
|
||||
processor = attributes.get('processor',self.parse)
|
||||
return processor(rows,fields,colnames,False)
|
||||
|
||||
def delete(self,tablename,query):
|
||||
if not isinstance(query,Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
if query.first.type=='id' and query.op==self.EQ:
|
||||
id = query.second
|
||||
tablename = query.first.tablename
|
||||
assert(tablename == query.first.tablename)
|
||||
ctable = self.connection[tablename]
|
||||
try:
|
||||
del ctable[str(id)]
|
||||
return 1
|
||||
except couchdb.http.ResourceNotFound:
|
||||
return 0
|
||||
else:
|
||||
tablename = self.get_table(query)
|
||||
rows = self.select(query,[self.db[tablename]._id],{})
|
||||
ctable = self.connection[tablename]
|
||||
for row in rows:
|
||||
del ctable[str(row.id)]
|
||||
return len(rows)
|
||||
|
||||
def update(self,tablename,query,fields):
|
||||
if not isinstance(query,Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
if query.first.type=='id' and query.op==self.EQ:
|
||||
id = query.second
|
||||
tablename = query.first.tablename
|
||||
ctable = self.connection[tablename]
|
||||
try:
|
||||
doc = ctable[str(id)]
|
||||
for key,value in fields:
|
||||
doc[key.name] = self.represent(value,self.db[tablename][key.name].type)
|
||||
ctable.save(doc)
|
||||
return 1
|
||||
except couchdb.http.ResourceNotFound:
|
||||
return 0
|
||||
else:
|
||||
tablename = self.get_table(query)
|
||||
rows = self.select(query,[self.db[tablename]._id],{})
|
||||
ctable = self.connection[tablename]
|
||||
table = self.db[tablename]
|
||||
for row in rows:
|
||||
doc = ctable[str(row.id)]
|
||||
for key,value in fields:
|
||||
doc[key.name] = self.represent(value,table[key.name].type)
|
||||
ctable.save(doc)
|
||||
return len(rows)
|
||||
|
||||
def count(self,query,distinct=None):
|
||||
if distinct:
|
||||
raise RuntimeError("COUNT DISTINCT not supported")
|
||||
if not isinstance(query,Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
tablename = self.get_table(query)
|
||||
rows = self.select(query,[self.db[tablename]._id],{})
|
||||
return len(rows)
|
||||
@@ -1,54 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .mysql import MySQLAdapter
|
||||
|
||||
|
||||
class CubridAdapter(MySQLAdapter):
|
||||
drivers = ('cubriddb',)
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:/]+)(\:(?P<port>[0-9]+))?/(?P<db>[^?]+)(\?set_encoding=(?P<charset>\w+))?$')
|
||||
|
||||
def __init__(self, db, uri, pool_size=0, folder=None, db_codec='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "cubrid"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
"Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
port = int(m.group('port') or '30000')
|
||||
user = credential_decoder(user)
|
||||
passwd = credential_decoder(password)
|
||||
def connector(host=host,port=port,db=db,
|
||||
user=user,passwd=passwd,driver_args=driver_args):
|
||||
return self.driver.connect(host,port,db,user,passwd,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.execute('SET FOREIGN_KEY_CHECKS=1;')
|
||||
self.execute("SET sql_mode='NO_BACKSLASH_ESCAPES';")
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import datetime
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class DB2Adapter(BaseAdapter):
|
||||
drivers = ('ibm_db_dbi', 'pyodbc')
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'CLOB',
|
||||
'json': 'CLOB',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BLOB',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'REAL',
|
||||
'double': 'DOUBLE',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'TIMESTAMP',
|
||||
'id': 'INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL',
|
||||
'reference': 'INT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'CLOB',
|
||||
'list:string': 'CLOB',
|
||||
'list:reference': 'CLOB',
|
||||
'big-id': 'BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL',
|
||||
'big-reference': 'BIGINT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def LEFT_JOIN(self):
|
||||
return 'LEFT OUTER JOIN'
|
||||
|
||||
def RANDOM(self):
|
||||
return 'RAND()'
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
sql_o += ' FETCH FIRST %i ROWS ONLY' % lmax
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def represent_exceptions(self, obj, fieldtype):
|
||||
if fieldtype == 'blob':
|
||||
obj = base64.b64encode(str(obj))
|
||||
return "BLOB('%s')" % obj
|
||||
elif fieldtype == 'datetime':
|
||||
if isinstance(obj, datetime.datetime):
|
||||
obj = obj.isoformat()[:19].replace('T','-').replace(':','.')
|
||||
elif isinstance(obj, datetime.date):
|
||||
obj = obj.isoformat()[:10]+'-00.00.00'
|
||||
return "'%s'" % obj
|
||||
return None
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "db2"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://', 1)[1]
|
||||
|
||||
def connector(cnxn=ruri,driver_args=driver_args):
|
||||
if self.driver_name == 'ibm_db_dbi':
|
||||
vars = cnxn.split(";")
|
||||
cnxn = {}
|
||||
for var in vars:
|
||||
v = var.split('=')
|
||||
cnxn[v[0].lower()] = v[1]
|
||||
return self.driver.connect(cnxn['dsn'], cnxn['uid'], cnxn['pwd'], **driver_args)
|
||||
else:
|
||||
return self.driver.connect(cnxn, **driver_args)
|
||||
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def execute(self,command,placeholders=None):
|
||||
if command[-1:]==';':
|
||||
command = command[:-1]
|
||||
if placeholders:
|
||||
return self.log_execute(command, placeholders)
|
||||
return self.log_execute(command)
|
||||
|
||||
def lastrowid(self,table):
|
||||
self.execute('SELECT DISTINCT IDENTITY_VAL_LOCAL() FROM %s;' % table)
|
||||
return long(self.cursor.fetchone()[0])
|
||||
|
||||
def rowslice(self,rows,minimum=0,maximum=None):
|
||||
if maximum is None:
|
||||
return rows[minimum:]
|
||||
return rows[minimum:maximum]
|
||||
@@ -1,182 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from ..objects import Expression
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class FireBirdAdapter(BaseAdapter):
|
||||
drivers = ('kinterbasdb','firebirdsql','fdb','pyodbc')
|
||||
|
||||
commit_on_alter_table = False
|
||||
support_distributed_transaction = True
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'BLOB SUB_TYPE 1',
|
||||
'json': 'BLOB SUB_TYPE 1',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BLOB SUB_TYPE 0',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INTEGER',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'DOUBLE PRECISION',
|
||||
'decimal': 'DECIMAL(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'TIMESTAMP',
|
||||
'id': 'INTEGER PRIMARY KEY',
|
||||
'reference': 'INTEGER REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'BLOB SUB_TYPE 1',
|
||||
'list:string': 'BLOB SUB_TYPE 1',
|
||||
'list:reference': 'BLOB SUB_TYPE 1',
|
||||
'big-id': 'BIGINT PRIMARY KEY',
|
||||
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def sequence_name(self,tablename):
|
||||
return ('genid_' + self.QUOTE_TEMPLATE) % tablename
|
||||
|
||||
def trigger_name(self,tablename):
|
||||
return 'trg_id_%s' % tablename
|
||||
|
||||
def RANDOM(self):
|
||||
return 'RAND()'
|
||||
|
||||
def EPOCH(self, first):
|
||||
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
|
||||
|
||||
def NOT_NULL(self,default,field_type):
|
||||
return 'DEFAULT %s NOT NULL' % self.represent(default,field_type)
|
||||
|
||||
def SUBSTRING(self,field,parameters):
|
||||
return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1])
|
||||
|
||||
def LENGTH(self, first):
|
||||
return "CHAR_LENGTH(%s)" % self.expand(first)
|
||||
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
if first.type.startswith('list:'):
|
||||
second = Expression(None,self.CONCAT('|',Expression(
|
||||
None,self.REPLACE(second,('|','||'))),'|'))
|
||||
return '(%s CONTAINING %s)' % (self.expand(first),
|
||||
self.expand(second, 'string'))
|
||||
|
||||
def _drop(self,table,mode):
|
||||
sequence_name = table._sequence_name
|
||||
return ['DROP TABLE %s %s;' % (table.sqlsafe, mode), 'DROP GENERATOR %s;' % sequence_name]
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
sql_s = ' FIRST %i SKIP %i %s' % (lmax - lmin, lmin, sql_s)
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def _truncate(self,table,mode = ''):
|
||||
return ['DELETE FROM %s;' % table._tablename,
|
||||
'SET GENERATOR %s TO 0;' % table._sequence_name]
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:/]+)(\:(?P<port>[0-9]+))?/(?P<db>.+?)(\?set_encoding=(?P<charset>\w+))?$')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "firebird"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError("Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
port = int(m.group('port') or 3050)
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
charset = m.group('charset') or 'UTF8'
|
||||
driver_args.update(dsn='%s/%s:%s' % (host,port,db),
|
||||
user = credential_decoder(user),
|
||||
password = credential_decoder(password),
|
||||
charset = charset)
|
||||
|
||||
def connector(driver_args=driver_args):
|
||||
return self.driver.connect(**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def create_sequence_and_triggers(self, query, table, **args):
|
||||
tablename = table._tablename
|
||||
sequence_name = table._sequence_name
|
||||
trigger_name = table._trigger_name
|
||||
self.execute(query)
|
||||
self.execute('create generator %s;' % sequence_name)
|
||||
self.execute('set generator %s to 0;' % sequence_name)
|
||||
self.execute('create trigger %s for %s active before insert position 0 as\nbegin\nif(new.id is null) then\nbegin\nnew.id = gen_id(%s, 1);\nend\nend;' % (trigger_name, tablename, sequence_name))
|
||||
|
||||
def lastrowid(self,table):
|
||||
sequence_name = table._sequence_name
|
||||
self.execute('SELECT gen_id(%s, 0) FROM rdb$database' % sequence_name)
|
||||
return long(self.cursor.fetchone()[0])
|
||||
|
||||
|
||||
class FireBirdEmbeddedAdapter(FireBirdAdapter):
|
||||
drivers = ('kinterbasdb','firebirdsql','fdb','pyodbc')
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<path>[^\?]+)(\?set_encoding=(?P<charset>\w+))?$')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "firebird"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
"Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
pathdb = m.group('path')
|
||||
if not pathdb:
|
||||
raise SyntaxError('Path required')
|
||||
charset = m.group('charset')
|
||||
if not charset:
|
||||
charset = 'UTF8'
|
||||
host = ''
|
||||
driver_args.update(host=host,
|
||||
database=pathdb,
|
||||
user=credential_decoder(user),
|
||||
password=credential_decoder(password),
|
||||
charset=charset)
|
||||
|
||||
def connector(driver_args=driver_args):
|
||||
return self.driver.connect(**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
@@ -1,621 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import re
|
||||
|
||||
from .._compat import pjoin
|
||||
from .._globals import IDENTITY, LOGGER, THREAD_LOCAL
|
||||
from .._load import classobj, gae, ndb, NDBDecimalProperty, GAEDecimalProperty, \
|
||||
namespace_manager, Key, NDBPolyModel, PolyModel, rdbms, have_serializers, \
|
||||
serializers, simplejson
|
||||
from ..objects import Table, Field, Expression, Query
|
||||
from ..helpers.classes import SQLCustomType, SQLALL, Reference, UseDatabaseStoredFile
|
||||
from ..helpers.methods import use_common_filters, xorify
|
||||
from .base import NoSQLAdapter
|
||||
from .mysql import MySQLAdapter
|
||||
|
||||
|
||||
class GoogleSQLAdapter(UseDatabaseStoredFile, MySQLAdapter):
|
||||
uploads_in_blob = True
|
||||
|
||||
REGEX_URI = re.compile('^(?P<instance>.*)/(?P<db>.*)$')
|
||||
|
||||
def __init__(self, db, uri='google:sql://realm:domain/database',
|
||||
pool_size=0, folder=None, db_codec='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
|
||||
self.db = db
|
||||
self.dbengine = "mysql"
|
||||
self.uri = uri
|
||||
self.pool_size = pool_size
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
if do_connect: self.find_driver(adapter_args, uri)
|
||||
self.folder = folder or pjoin('$HOME',THREAD_LOCAL.folder.split(
|
||||
os.sep+'applications'+os.sep,1)[1])
|
||||
ruri = uri.split("://")[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError("Invalid URI string in SQLDB: %s" % self.uri)
|
||||
instance = credential_decoder(m.group('instance'))
|
||||
self.dbstring = db = credential_decoder(m.group('db'))
|
||||
driver_args['instance'] = instance
|
||||
if not 'charset' in driver_args:
|
||||
driver_args['charset'] = 'utf8'
|
||||
self.createdb = createdb = adapter_args.get('createdb',True)
|
||||
if not createdb:
|
||||
driver_args['database'] = db
|
||||
def connector(driver_args=driver_args):
|
||||
return rdbms.connect(**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
if self.createdb:
|
||||
# self.execute('DROP DATABASE %s' % self.dbstring)
|
||||
self.execute('CREATE DATABASE IF NOT EXISTS %s' % self.dbstring)
|
||||
self.execute('USE %s' % self.dbstring)
|
||||
self.execute("SET FOREIGN_KEY_CHECKS=1;")
|
||||
self.execute("SET sql_mode='NO_BACKSLASH_ESCAPES';")
|
||||
|
||||
def execute(self, command, *a, **b):
|
||||
return self.log_execute(command.decode('utf8'), *a, **b)
|
||||
|
||||
def find_driver(self,adapter_args,uri=None):
|
||||
self.adapter_args = adapter_args
|
||||
self.driver = "google"
|
||||
|
||||
|
||||
class GAEF(object):
|
||||
def __init__(self,name,op,value,apply):
|
||||
self.name=name=='id' and '__key__' or name
|
||||
self.op=op
|
||||
self.value=value
|
||||
self.apply=apply
|
||||
def __repr__(self):
|
||||
return '(%s %s %s:%s)' % (self.name, self.op, repr(self.value), type(self.value))
|
||||
|
||||
|
||||
class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
"""
|
||||
NDB:
|
||||
|
||||
You can enable NDB by using adapter_args::
|
||||
|
||||
db = DAL('google:datastore', adapter_args={'ndb_settings':ndb_settings, 'use_ndb':True})
|
||||
|
||||
ndb_settings is optional and can be used for per model caching settings.
|
||||
It must be a dict in this form::
|
||||
|
||||
ndb_settings = {<table_name>:{<variable_name>:<variable_value>}}
|
||||
|
||||
See: https://developers.google.com/appengine/docs/python/ndb/cache
|
||||
"""
|
||||
|
||||
MAX_FETCH_LIMIT = 1000000
|
||||
uploads_in_blob = True
|
||||
types = {}
|
||||
# reconnect is not required for Datastore dbs
|
||||
reconnect = lambda *args, **kwargs: None
|
||||
|
||||
def file_exists(self, filename): pass
|
||||
def file_open(self, filename, mode='rb', lock=True): pass
|
||||
def file_close(self, fileobj): pass
|
||||
|
||||
REGEX_NAMESPACE = re.compile('.*://(?P<namespace>.+)')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.use_ndb = adapter_args.get('use_ndb',uri.startswith('google:datastore+ndb'))
|
||||
if self.use_ndb is True:
|
||||
self.types.update({
|
||||
'boolean': ndb.BooleanProperty,
|
||||
'string': (lambda **kwargs: ndb.StringProperty(**kwargs)),
|
||||
'text': ndb.TextProperty,
|
||||
'json': ndb.TextProperty,
|
||||
'password': ndb.StringProperty,
|
||||
'blob': ndb.BlobProperty,
|
||||
'upload': ndb.StringProperty,
|
||||
'integer': ndb.IntegerProperty,
|
||||
'bigint': ndb.IntegerProperty,
|
||||
'float': ndb.FloatProperty,
|
||||
'double': ndb.FloatProperty,
|
||||
'decimal': NDBDecimalProperty,
|
||||
'date': ndb.DateProperty,
|
||||
'time': ndb.TimeProperty,
|
||||
'datetime': ndb.DateTimeProperty,
|
||||
'id': None,
|
||||
'reference': ndb.IntegerProperty,
|
||||
'list:string': (lambda **kwargs: ndb.StringProperty(repeated=True,default=None, **kwargs)),
|
||||
'list:integer': (lambda **kwargs: ndb.IntegerProperty(repeated=True,default=None, **kwargs)),
|
||||
'list:reference': (lambda **kwargs: ndb.IntegerProperty(repeated=True,default=None, **kwargs)),
|
||||
})
|
||||
else:
|
||||
self.types.update({
|
||||
'boolean': gae.BooleanProperty,
|
||||
'string': (lambda **kwargs: gae.StringProperty(multiline=True, **kwargs)),
|
||||
'text': gae.TextProperty,
|
||||
'json': gae.TextProperty,
|
||||
'password': gae.StringProperty,
|
||||
'blob': gae.BlobProperty,
|
||||
'upload': gae.StringProperty,
|
||||
'integer': gae.IntegerProperty,
|
||||
'bigint': gae.IntegerProperty,
|
||||
'float': gae.FloatProperty,
|
||||
'double': gae.FloatProperty,
|
||||
'decimal': GAEDecimalProperty,
|
||||
'date': gae.DateProperty,
|
||||
'time': gae.TimeProperty,
|
||||
'datetime': gae.DateTimeProperty,
|
||||
'id': None,
|
||||
'reference': gae.IntegerProperty,
|
||||
'list:string': (lambda **kwargs: gae.StringListProperty(default=None, **kwargs)),
|
||||
'list:integer': (lambda **kwargs: gae.ListProperty(int,default=None, **kwargs)),
|
||||
'list:reference': (lambda **kwargs: gae.ListProperty(int,default=None, **kwargs)),
|
||||
})
|
||||
self.db = db
|
||||
self.uri = uri
|
||||
self.dbengine = 'google:datastore'
|
||||
self.folder = folder
|
||||
db['_lastsql'] = ''
|
||||
self.db_codec = 'UTF-8'
|
||||
self._after_connection = after_connection
|
||||
self.pool_size = 0
|
||||
match = self.REGEX_NAMESPACE.match(uri)
|
||||
if match:
|
||||
namespace_manager.set_namespace(match.group('namespace'))
|
||||
self.keyfunc = (self.use_ndb and ndb.Key) or Key.from_path
|
||||
|
||||
self.ndb_settings = None
|
||||
if 'ndb_settings' in adapter_args:
|
||||
self.ndb_settings = adapter_args['ndb_settings']
|
||||
|
||||
def parse_id(self, value, field_type):
|
||||
return value
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
if fieldtype == "json":
|
||||
if have_serializers:
|
||||
return serializers.json(obj)
|
||||
elif simplejson:
|
||||
return simplejson.dumps(obj)
|
||||
else:
|
||||
raise Exception("Could not dump json object (missing json library)")
|
||||
else:
|
||||
return NoSQLAdapter.represent(self, obj, fieldtype)
|
||||
|
||||
def create_table(self,table,migrate=True,fake_migrate=False, polymodel=None):
|
||||
myfields = {}
|
||||
for field in table:
|
||||
if isinstance(polymodel,Table) and field.name in polymodel.fields():
|
||||
continue
|
||||
attr = {}
|
||||
if isinstance(field.custom_qualifier, dict):
|
||||
#this is custom properties to add to the GAE field declartion
|
||||
attr = field.custom_qualifier
|
||||
field_type = field.type
|
||||
if isinstance(field_type, SQLCustomType):
|
||||
ftype = self.types[field_type.native or field_type.type](**attr)
|
||||
elif isinstance(field_type, ((self.use_ndb and ndb.Property) or gae.Property)):
|
||||
ftype = field_type
|
||||
elif field_type.startswith('id'):
|
||||
continue
|
||||
elif field_type.startswith('decimal'):
|
||||
precision, scale = field_type[7:].strip('()').split(',')
|
||||
precision = int(precision)
|
||||
scale = int(scale)
|
||||
dec_cls = (self.use_ndb and NDBDecimalProperty) or GAEDecimalProperty
|
||||
ftype = dec_cls(precision, scale, **attr)
|
||||
elif field_type.startswith('reference'):
|
||||
if field.notnull:
|
||||
attr = dict(required=True)
|
||||
ftype = self.types[field_type[:9]](**attr)
|
||||
elif field_type.startswith('list:reference'):
|
||||
if field.notnull:
|
||||
attr['required'] = True
|
||||
ftype = self.types[field_type[:14]](**attr)
|
||||
elif field_type.startswith('list:'):
|
||||
ftype = self.types[field_type](**attr)
|
||||
elif not field_type in self.types\
|
||||
or not self.types[field_type]:
|
||||
raise SyntaxError('Field: unknown field type: %s' % field_type)
|
||||
else:
|
||||
ftype = self.types[field_type](**attr)
|
||||
myfields[field.name] = ftype
|
||||
if not polymodel:
|
||||
model_cls = (self.use_ndb and ndb.Model) or gae.Model
|
||||
table._tableobj = classobj(table._tablename, (model_cls, ), myfields)
|
||||
if self.use_ndb:
|
||||
# Set NDB caching variables
|
||||
if self.ndb_settings and (table._tablename in self.ndb_settings):
|
||||
for k, v in self.ndb_settings.iteritems():
|
||||
setattr(table._tableobj, k, v)
|
||||
elif polymodel==True:
|
||||
pm_cls = (self.use_ndb and NDBPolyModel) or PolyModel
|
||||
table._tableobj = classobj(table._tablename, (pm_cls, ), myfields)
|
||||
elif isinstance(polymodel,Table):
|
||||
table._tableobj = classobj(table._tablename, (polymodel._tableobj, ), myfields)
|
||||
else:
|
||||
raise SyntaxError("polymodel must be None, True, a table or a tablename")
|
||||
return None
|
||||
|
||||
def expand(self,expression,field_type=None):
|
||||
if isinstance(expression,Field):
|
||||
if expression.type in ('text', 'blob', 'json'):
|
||||
raise SyntaxError('AppEngine does not index by: %s' % expression.type)
|
||||
return expression.name
|
||||
elif isinstance(expression, (Expression, Query)):
|
||||
if not expression.second is None:
|
||||
return expression.op(expression.first, expression.second)
|
||||
elif not expression.first is None:
|
||||
return expression.op(expression.first)
|
||||
else:
|
||||
return expression.op()
|
||||
elif field_type:
|
||||
return self.represent(expression,field_type)
|
||||
elif isinstance(expression,(list,tuple)):
|
||||
return ','.join([self.represent(item,field_type) for item in expression])
|
||||
else:
|
||||
return str(expression)
|
||||
|
||||
### TODO from gql.py Expression
|
||||
def AND(self,first,second):
|
||||
a = self.expand(first)
|
||||
b = self.expand(second)
|
||||
if b[0].name=='__key__' and a[0].name!='__key__':
|
||||
return b+a
|
||||
return a+b
|
||||
|
||||
def EQ(self,first,second=None):
|
||||
if isinstance(second, Key):
|
||||
return [GAEF(first.name,'=',second,lambda a,b:a==b)]
|
||||
return [GAEF(first.name,'=',self.represent(second,first.type),lambda a,b:a==b)]
|
||||
|
||||
def NE(self,first,second=None):
|
||||
if first.type != 'id':
|
||||
return [GAEF(first.name,'!=',self.represent(second,first.type),lambda a,b:a!=b)]
|
||||
else:
|
||||
if not second is None:
|
||||
second = self.keyfunc(first._tablename, long(second))
|
||||
return [GAEF(first.name,'!=',second,lambda a,b:a!=b)]
|
||||
|
||||
def LT(self,first,second=None):
|
||||
if first.type != 'id':
|
||||
return [GAEF(first.name,'<',self.represent(second,first.type),lambda a,b:a<b)]
|
||||
else:
|
||||
second = self.keyfunc(first._tablename, long(second))
|
||||
return [GAEF(first.name,'<',second,lambda a,b:a<b)]
|
||||
|
||||
def LE(self,first,second=None):
|
||||
if first.type != 'id':
|
||||
return [GAEF(first.name,'<=',self.represent(second,first.type),lambda a,b:a<=b)]
|
||||
else:
|
||||
second = self.keyfunc(first._tablename, long(second))
|
||||
return [GAEF(first.name,'<=',second,lambda a,b:a<=b)]
|
||||
|
||||
def GT(self,first,second=None):
|
||||
if first.type != 'id' or second==0 or second == '0':
|
||||
return [GAEF(first.name,'>',self.represent(second,first.type),lambda a,b:a>b)]
|
||||
else:
|
||||
second = self.keyfunc(first._tablename, long(second))
|
||||
return [GAEF(first.name,'>',second,lambda a,b:a>b)]
|
||||
|
||||
def GE(self,first,second=None):
|
||||
if first.type != 'id':
|
||||
return [GAEF(first.name,'>=',self.represent(second,first.type),lambda a,b:a>=b)]
|
||||
else:
|
||||
second = self.keyfunc(first._tablename, long(second))
|
||||
return [GAEF(first.name,'>=',second,lambda a,b:a>=b)]
|
||||
|
||||
def INVERT(self,first):
|
||||
return '-%s' % first.name
|
||||
|
||||
def COMMA(self,first,second):
|
||||
return '%s, %s' % (self.expand(first),self.expand(second))
|
||||
|
||||
def BELONGS(self,first,second=None):
|
||||
if not isinstance(second,(list, tuple, set)):
|
||||
raise SyntaxError("Not supported")
|
||||
if not self.use_ndb:
|
||||
if isinstance(second,set):
|
||||
second = list(second)
|
||||
if first.type == 'id':
|
||||
second = [self.keyfunc(first._tablename, int(i)) for i in second]
|
||||
return [GAEF(first.name,'in',second,lambda a,b:a in b)]
|
||||
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
# silently ignoring: GAE can only do case sensitive matches!
|
||||
if not first.type.startswith('list:'):
|
||||
raise SyntaxError("Not supported")
|
||||
return [GAEF(first.name,'=',self.expand(second,first.type[5:]),lambda a,b:b in a)]
|
||||
|
||||
def NOT(self,first):
|
||||
nops = { self.EQ: self.NE,
|
||||
self.NE: self.EQ,
|
||||
self.LT: self.GE,
|
||||
self.GT: self.LE,
|
||||
self.LE: self.GT,
|
||||
self.GE: self.LT}
|
||||
if not isinstance(first,Query):
|
||||
raise SyntaxError("Not suported")
|
||||
nop = nops.get(first.op,None)
|
||||
if not nop:
|
||||
raise SyntaxError("Not suported %s" % first.op.__name__)
|
||||
first.op = nop
|
||||
return self.expand(first)
|
||||
|
||||
def truncate(self,table,mode):
|
||||
self.db(self.db._adapter.id_query(table)).delete()
|
||||
|
||||
GAE_FILTER_OPTIONS = {
|
||||
'=': lambda q, t, p, v: q.filter(getattr(t,p) == v),
|
||||
'>': lambda q, t, p, v: q.filter(getattr(t,p) > v),
|
||||
'<': lambda q, t, p, v: q.filter(getattr(t,p) < v),
|
||||
'<=': lambda q, t, p, v: q.filter(getattr(t,p) <= v),
|
||||
'>=': lambda q, t, p, v: q.filter(getattr(t,p) >= v),
|
||||
'!=': lambda q, t, p, v: q.filter(getattr(t,p) != v),
|
||||
'in': lambda q, t, p, v: q.filter(getattr(t,p).IN(v)),
|
||||
}
|
||||
|
||||
def filter(self, query, tableobj, prop, op, value):
|
||||
return self.GAE_FILTER_OPTIONS[op](query, tableobj, prop, value)
|
||||
|
||||
def select_raw(self,query,fields=None,attributes=None,count_only=False):
|
||||
db = self.db
|
||||
fields = fields or []
|
||||
attributes = attributes or {}
|
||||
args_get = attributes.get
|
||||
new_fields = []
|
||||
|
||||
for item in fields:
|
||||
if isinstance(item,SQLALL):
|
||||
new_fields += item._table
|
||||
else:
|
||||
new_fields.append(item)
|
||||
|
||||
fields = new_fields
|
||||
if query:
|
||||
tablename = self.get_table(query)
|
||||
elif fields:
|
||||
tablename = fields[0].tablename
|
||||
query = db._adapter.id_query(fields[0].table)
|
||||
else:
|
||||
raise SyntaxError("Unable to determine a tablename")
|
||||
|
||||
if query:
|
||||
if use_common_filters(query):
|
||||
query = self.common_filter(query,[tablename])
|
||||
|
||||
#tableobj is a GAE/NDB Model class (or subclass)
|
||||
tableobj = db[tablename]._tableobj
|
||||
filters = self.expand(query)
|
||||
|
||||
projection = None
|
||||
if len(db[tablename].fields) == len(fields):
|
||||
#getting all fields, not a projection query
|
||||
projection = None
|
||||
elif args_get('projection') == True:
|
||||
projection = []
|
||||
for f in fields:
|
||||
if f.type in ['text', 'blob', 'json']:
|
||||
raise SyntaxError(
|
||||
"text and blob field types not allowed in projection queries")
|
||||
else:
|
||||
projection.append(f.name)
|
||||
|
||||
elif args_get('filterfields') is True:
|
||||
projection = []
|
||||
for f in fields:
|
||||
projection.append(f.name)
|
||||
|
||||
# real projection's can't include 'id'.
|
||||
# it will be added to the result later
|
||||
query_projection = [
|
||||
p for p in projection if \
|
||||
p != db[tablename]._id.name] if projection and \
|
||||
args_get('projection') == True\
|
||||
else None
|
||||
|
||||
cursor = args_get('reusecursor')
|
||||
cursor = cursor if isinstance(cursor, str) else None
|
||||
if self.use_ndb:
|
||||
qo = ndb.QueryOptions(projection=query_projection, cursor=cursor)
|
||||
items = tableobj.query(default_options=qo)
|
||||
else:
|
||||
items = gae.Query(tableobj, projection=query_projection, cursor=cursor)
|
||||
|
||||
for filter in filters:
|
||||
if (args_get('projection') == True and
|
||||
filter.name in query_projection and
|
||||
filter.op in ('=', '<=', '>=')):
|
||||
raise SyntaxError("projection fields cannot have equality filters")
|
||||
if filter.name=='__key__' and filter.op=='>' and filter.value==0:
|
||||
continue
|
||||
elif filter.name=='__key__' and filter.op=='=':
|
||||
if filter.value==0:
|
||||
items = []
|
||||
elif isinstance(filter.value, (self.use_ndb and ndb.Key) or Key):
|
||||
# key qeuries return a class instance,
|
||||
# can't use projection
|
||||
# extra values will be ignored in post-processing later
|
||||
item = filter.value.get() if self.use_ndb else tableobj.get(filter.value)
|
||||
items = [item] if item else []
|
||||
else:
|
||||
# key qeuries return a class instance,
|
||||
# can't use projection
|
||||
# extra values will be ignored in post-processing later
|
||||
item = tableobj.get_by_id(filter.value)
|
||||
items = [item] if item else []
|
||||
elif isinstance(items,list): # i.e. there is a single record!
|
||||
items = [i for i in items if filter.apply(
|
||||
getattr(item,filter.name),filter.value)]
|
||||
else:
|
||||
if filter.name=='__key__' and filter.op != 'in':
|
||||
items.order(tableobj._key) if self.use_ndb else items.order('__key__')
|
||||
if self.use_ndb:
|
||||
items = self.filter(items, tableobj, filter.name, filter.op, filter.value)
|
||||
else:
|
||||
items = items.filter('%s %s' % (filter.name,filter.op), filter.value)
|
||||
|
||||
if count_only:
|
||||
items = [len(items) if isinstance(items,list) else items.count()]
|
||||
elif not isinstance(items,list):
|
||||
query = items
|
||||
if args_get('left', None):
|
||||
raise SyntaxError('Set: no left join in appengine')
|
||||
if args_get('groupby', None):
|
||||
raise SyntaxError('Set: no groupby in appengine')
|
||||
orderby = args_get('orderby', False)
|
||||
if orderby:
|
||||
### THIS REALLY NEEDS IMPROVEMENT !!!
|
||||
if isinstance(orderby, (list, tuple)):
|
||||
orderby = xorify(orderby)
|
||||
if isinstance(orderby,Expression):
|
||||
orderby = self.expand(orderby)
|
||||
orders = orderby.split(', ')
|
||||
for order in orders:
|
||||
if self.use_ndb:
|
||||
#TODO There must be a better way
|
||||
def make_order(o):
|
||||
s = str(o)
|
||||
desc = s[0] == '-'
|
||||
s = (desc and s[1:]) or s
|
||||
return (desc and -getattr(tableobj, s)) or getattr(tableobj, s)
|
||||
_order = {'-id':-tableobj._key,'id':tableobj._key}.get(order)
|
||||
if _order is None:
|
||||
_order = make_order(order)
|
||||
query = query.order(_order)
|
||||
else:
|
||||
order={'-id':'-__key__','id':'__key__'}.get(order,order)
|
||||
query = query.order(order)
|
||||
|
||||
if args_get('limitby', None):
|
||||
(lmin, lmax) = attributes['limitby']
|
||||
limit, fetch_args = lmax-lmin, {'offset':lmin,'keys_only':True}
|
||||
|
||||
if self.use_ndb:
|
||||
keys, cursor, more = query.fetch_page(limit,**fetch_args)
|
||||
items = ndb.get_multi(keys)
|
||||
else:
|
||||
keys = query.fetch(limit, **fetch_args)
|
||||
items = gae.get(keys)
|
||||
cursor = query.cursor()
|
||||
#cursor is only useful if there was a limit and we didn't return
|
||||
# all results
|
||||
if args_get('reusecursor'):
|
||||
db['_lastcursor'] = cursor
|
||||
else:
|
||||
# if a limit is not specified, always return an iterator
|
||||
rows = query
|
||||
|
||||
return (items, tablename, projection or db[tablename].fields)
|
||||
|
||||
def select(self,query,fields,attributes):
|
||||
"""
|
||||
This is the GAE version of select. Some notes to consider:
|
||||
- db['_lastsql'] is not set because there is not SQL statement string
|
||||
for a GAE query
|
||||
- 'nativeRef' is a magical fieldname used for self references on GAE
|
||||
- optional attribute 'projection' when set to True will trigger
|
||||
use of the GAE projection queries. note that there are rules for
|
||||
what is accepted imposed by GAE: each field must be indexed,
|
||||
projection queries cannot contain blob or text fields, and you
|
||||
cannot use == and also select that same field.
|
||||
see https://developers.google.com/appengine/docs/python/datastore/queries#Query_Projection
|
||||
- optional attribute 'filterfields' when set to True web2py will only
|
||||
parse the explicitly listed fields into the Rows object, even though
|
||||
all fields are returned in the query. This can be used to reduce
|
||||
memory usage in cases where true projection queries are not
|
||||
usable.
|
||||
- optional attribute 'reusecursor' allows use of cursor with queries
|
||||
that have the limitby attribute. Set the attribute to True for the
|
||||
first query, set it to the value of db['_lastcursor'] to continue
|
||||
a previous query. The user must save the cursor value between
|
||||
requests, and the filters must be identical. It is up to the user
|
||||
to follow google's limitations:
|
||||
https://developers.google.com/appengine/docs/python/datastore/queries#Query_Cursors
|
||||
"""
|
||||
|
||||
(items, tablename, fields) = self.select_raw(query,fields,attributes)
|
||||
# self.db['_lastsql'] = self._select(query,fields,attributes)
|
||||
rows = [[(t==self.db[tablename]._id.name and item) or \
|
||||
(t=='nativeRef' and item) or getattr(item, t) \
|
||||
for t in fields] for item in items]
|
||||
colnames = ['%s.%s' % (tablename, t) for t in fields]
|
||||
processor = attributes.get('processor',self.parse)
|
||||
return processor(rows,fields,colnames,False)
|
||||
|
||||
def parse_list_integers(self, value, field_type):
|
||||
return value[:] if self.use_ndb else value
|
||||
|
||||
def parse_list_strings(self, value, field_type):
|
||||
return value[:] if self.use_ndb else value
|
||||
|
||||
def count(self,query,distinct=None,limit=None):
|
||||
if distinct:
|
||||
raise RuntimeError("COUNT DISTINCT not supported")
|
||||
(items, tablename, fields) = self.select_raw(query,count_only=True)
|
||||
return items[0]
|
||||
|
||||
def delete(self,tablename, query):
|
||||
"""
|
||||
This function was changed on 2010-05-04 because according to
|
||||
http://code.google.com/p/googleappengine/issues/detail?id=3119
|
||||
GAE no longer supports deleting more than 1000 records.
|
||||
"""
|
||||
# self.db['_lastsql'] = self._delete(tablename,query)
|
||||
(items, tablename, fields) = self.select_raw(query)
|
||||
# items can be one item or a query
|
||||
if not isinstance(items,list):
|
||||
#use a keys_only query to ensure that this runs as a datastore
|
||||
# small operations
|
||||
leftitems = items.fetch(1000, keys_only=True)
|
||||
counter = 0
|
||||
while len(leftitems):
|
||||
counter += len(leftitems)
|
||||
if self.use_ndb:
|
||||
ndb.delete_multi(leftitems)
|
||||
else:
|
||||
gae.delete(leftitems)
|
||||
leftitems = items.fetch(1000, keys_only=True)
|
||||
else:
|
||||
counter = len(items)
|
||||
if self.use_ndb:
|
||||
ndb.delete_multi([item.key for item in items])
|
||||
else:
|
||||
gae.delete(items)
|
||||
return counter
|
||||
|
||||
def update(self,tablename,query,update_fields):
|
||||
# self.db['_lastsql'] = self._update(tablename,query,update_fields)
|
||||
(items, tablename, fields) = self.select_raw(query)
|
||||
counter = 0
|
||||
for item in items:
|
||||
for field, value in update_fields:
|
||||
setattr(item, field.name, self.represent(value,field.type))
|
||||
item.put()
|
||||
counter += 1
|
||||
LOGGER.info(str(counter))
|
||||
return counter
|
||||
|
||||
def insert(self,table,fields):
|
||||
dfields=dict((f.name,self.represent(v,f.type)) for f,v in fields)
|
||||
# table._db['_lastsql'] = self._insert(table,fields)
|
||||
tmp = table._tableobj(**dfields)
|
||||
tmp.put()
|
||||
key = tmp.key if self.use_ndb else tmp.key()
|
||||
rid = Reference(key.id())
|
||||
(rid._table, rid._record, rid._gaekey) = (table, None, key)
|
||||
return rid
|
||||
|
||||
def bulk_insert(self,table,items):
|
||||
parsed_items = []
|
||||
for item in items:
|
||||
dfields=dict((f.name,self.represent(v,f.type)) for f,v in item)
|
||||
parsed_items.append(table._tableobj(**dfields))
|
||||
if self.use_ndb:
|
||||
ndb.put_multi(parsed_items)
|
||||
else:
|
||||
gae.put(parsed_items)
|
||||
return True
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,134 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class InformixAdapter(BaseAdapter):
|
||||
drivers = ('informixdb',)
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'BLOB SUB_TYPE 1',
|
||||
'json': 'BLOB SUB_TYPE 1',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BLOB SUB_TYPE 0',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INTEGER',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'DOUBLE PRECISION',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'CHAR(8)',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'SERIAL',
|
||||
'reference': 'INTEGER REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'BLOB SUB_TYPE 1',
|
||||
'list:string': 'BLOB SUB_TYPE 1',
|
||||
'list:reference': 'BLOB SUB_TYPE 1',
|
||||
'big-id': 'BIGSERIAL',
|
||||
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': 'REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s CONSTRAINT FK_%(table_name)s_%(field_name)s',
|
||||
'reference TFK': 'FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s CONSTRAINT TFK_%(table_name)s_%(field_name)s',
|
||||
}
|
||||
|
||||
def RANDOM(self):
|
||||
return 'Random()'
|
||||
|
||||
def NOT_NULL(self,default,field_type):
|
||||
return 'DEFAULT %s NOT NULL' % self.represent(default,field_type)
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
fetch_amt = lmax - lmin
|
||||
dbms_version = int(self.connection.dbms_version.split('.')[0])
|
||||
if lmin and (dbms_version >= 10):
|
||||
# Requires Informix 10.0+
|
||||
sql_s += ' SKIP %d' % (lmin, )
|
||||
if fetch_amt and (dbms_version >= 9):
|
||||
# Requires Informix 9.0+
|
||||
sql_s += ' FIRST %d' % (fetch_amt, )
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def represent_exceptions(self, obj, fieldtype):
|
||||
if fieldtype == 'date':
|
||||
if isinstance(obj, (datetime.date, datetime.datetime)):
|
||||
obj = obj.isoformat()[:10]
|
||||
else:
|
||||
obj = str(obj)
|
||||
return "to_date('%s','%%Y-%%m-%%d')" % obj
|
||||
elif fieldtype == 'datetime':
|
||||
if isinstance(obj, datetime.datetime):
|
||||
obj = obj.isoformat()[:19].replace('T',' ')
|
||||
elif isinstance(obj, datetime.date):
|
||||
obj = obj.isoformat()[:10]+' 00:00:00'
|
||||
else:
|
||||
obj = str(obj)
|
||||
return "to_date('%s','%%Y-%%m-%%d %%H:%%M:%%S')" % obj
|
||||
return None
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:/]+)(\:(?P<port>[0-9]+))?/(?P<db>.+)$')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "informix"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
"Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
user = credential_decoder(user)
|
||||
password = credential_decoder(password)
|
||||
dsn = '%s@%s' % (db,host)
|
||||
driver_args.update(user=user,password=password,autocommit=True)
|
||||
def connector(dsn=dsn,driver_args=driver_args):
|
||||
return self.driver.connect(dsn,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def execute(self,command):
|
||||
if command[-1:]==';':
|
||||
command = command[:-1]
|
||||
return self.log_execute(command)
|
||||
|
||||
def lastrowid(self,table):
|
||||
return self.cursor.sqlerrd[1]
|
||||
|
||||
|
||||
class InformixSEAdapter(InformixAdapter):
|
||||
""" work in progress """
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
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):
|
||||
if maximum is None:
|
||||
return rows[minimum:]
|
||||
return rows[minimum:maximum]
|
||||
@@ -1,147 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .._load import pyodbc
|
||||
from .base import BaseAdapter
|
||||
|
||||
# NOTE invalid database object name (ANSI-SQL wants
|
||||
# this form of name to be a delimited identifier)
|
||||
INGRES_SEQNAME='ii***lineitemsequence'
|
||||
|
||||
|
||||
class IngresAdapter(BaseAdapter):
|
||||
drivers = ('pyodbc',)
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'CLOB',
|
||||
'json': 'CLOB',
|
||||
'password': 'VARCHAR(%(length)s)', ## Not sure what this contains utf8 or nvarchar. Or even bytes?
|
||||
'blob': 'BLOB',
|
||||
'upload': 'VARCHAR(%(length)s)', ## FIXME utf8 or nvarchar... or blob? what is this type?
|
||||
'integer': 'INTEGER4', # or int8...
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT8',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'ANSIDATE',
|
||||
'time': 'TIME WITHOUT TIME ZONE',
|
||||
'datetime': 'TIMESTAMP WITHOUT TIME ZONE',
|
||||
'id': 'int not null unique with default next value for %s' % INGRES_SEQNAME,
|
||||
'reference': 'INT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'CLOB',
|
||||
'list:string': 'CLOB',
|
||||
'list:reference': 'CLOB',
|
||||
'big-id': 'bigint not null unique with default next value for %s' % INGRES_SEQNAME,
|
||||
'big-reference': 'BIGINT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s', ## FIXME TODO
|
||||
}
|
||||
|
||||
def LEFT_JOIN(self):
|
||||
return 'LEFT OUTER JOIN'
|
||||
|
||||
def RANDOM(self):
|
||||
return 'RANDOM()'
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
fetch_amt = lmax - lmin
|
||||
if fetch_amt:
|
||||
sql_s += ' FIRST %d ' % (fetch_amt, )
|
||||
if lmin:
|
||||
# Requires Ingres 9.2+
|
||||
sql_o += ' OFFSET %d' % (lmin, )
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "ingres"
|
||||
self._driver = pyodbc
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
connstr = uri.split(':', 1)[1]
|
||||
# Simple URI processing
|
||||
connstr = connstr.lstrip()
|
||||
while connstr.startswith('/'):
|
||||
connstr = connstr[1:]
|
||||
if '=' in connstr:
|
||||
# Assume we have a regular ODBC connection string and just use it
|
||||
ruri = connstr
|
||||
else:
|
||||
# Assume only (local) dbname is passed in with OS auth
|
||||
database_name = connstr
|
||||
default_driver_name = 'Ingres'
|
||||
vnode = '(local)'
|
||||
servertype = 'ingres'
|
||||
ruri = 'Driver={%s};Server=%s;Database=%s' % (default_driver_name, vnode, database_name)
|
||||
def connector(cnxn=ruri,driver_args=driver_args):
|
||||
return self.driver.connect(cnxn,**driver_args)
|
||||
|
||||
self.connector = connector
|
||||
|
||||
# TODO if version is >= 10, set types['id'] to Identity column, see http://community.actian.com/wiki/Using_Ingres_Identity_Columns
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def create_sequence_and_triggers(self, query, table, **args):
|
||||
# post create table auto inc code (if needed)
|
||||
# modify table to btree for performance....
|
||||
# Older Ingres releases could use rule/trigger like Oracle above.
|
||||
if hasattr(table,'_primarykey'):
|
||||
modify_tbl_sql = 'modify %s to btree unique on %s' % \
|
||||
(table._tablename,
|
||||
', '.join(["'%s'" % x for x in table.primarykey]))
|
||||
self.execute(modify_tbl_sql)
|
||||
else:
|
||||
tmp_seqname='%s_iisq' % table._tablename
|
||||
query=query.replace(INGRES_SEQNAME, tmp_seqname)
|
||||
self.execute('create sequence %s' % tmp_seqname)
|
||||
self.execute(query)
|
||||
self.execute('modify %s to btree unique on %s' % (table._tablename, 'id'))
|
||||
|
||||
|
||||
def lastrowid(self,table):
|
||||
tmp_seqname='%s_iisq' % table
|
||||
self.execute('select current value for %s' % tmp_seqname)
|
||||
return long(self.cursor.fetchone()[0]) # don't really need int type cast here...
|
||||
|
||||
|
||||
class IngresUnicodeAdapter(IngresAdapter):
|
||||
|
||||
drivers = ('pyodbc',)
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'NVARCHAR(%(length)s)',
|
||||
'text': 'NCLOB',
|
||||
'json': 'NCLOB',
|
||||
'password': 'NVARCHAR(%(length)s)', ## Not sure what this contains utf8 or nvarchar. Or even bytes?
|
||||
'blob': 'BLOB',
|
||||
'upload': 'VARCHAR(%(length)s)', ## FIXME utf8 or nvarchar... or blob? what is this type?
|
||||
'integer': 'INTEGER4', # or int8...
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT8',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'ANSIDATE',
|
||||
'time': 'TIME WITHOUT TIME ZONE',
|
||||
'datetime': 'TIMESTAMP WITHOUT TIME ZONE',
|
||||
'id': 'INTEGER4 not null unique with default next value for %s'% INGRES_SEQNAME,
|
||||
'reference': 'INTEGER4, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'NCLOB',
|
||||
'list:string': 'NCLOB',
|
||||
'list:reference': 'NCLOB',
|
||||
'big-id': 'BIGINT not null unique with default next value for %s'% INGRES_SEQNAME,
|
||||
'big-reference': 'BIGINT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s', ## FIXME TODO
|
||||
}
|
||||
@@ -1,575 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from ..objects import Table, Query, Field, Expression
|
||||
from ..helpers.classes import SQLALL
|
||||
from ..helpers.methods import xorify
|
||||
from .base import NoSQLAdapter
|
||||
|
||||
class MongoDBAdapter(NoSQLAdapter):
|
||||
drivers = ('pymongo',)
|
||||
driver_auto_json = ['loads','dumps']
|
||||
|
||||
uploads_in_blob = False
|
||||
|
||||
types = {
|
||||
'boolean': bool,
|
||||
'string': str,
|
||||
'text': str,
|
||||
'json': str,
|
||||
'password': str,
|
||||
'blob': str,
|
||||
'upload': str,
|
||||
'integer': long,
|
||||
'bigint': long,
|
||||
'float': float,
|
||||
'double': float,
|
||||
'date': datetime.date,
|
||||
'time': datetime.time,
|
||||
'datetime': datetime.datetime,
|
||||
'id': long,
|
||||
'reference': long,
|
||||
'list:string': list,
|
||||
'list:integer': list,
|
||||
'list:reference': list,
|
||||
}
|
||||
|
||||
error_messages = {"javascript_needed": "This must yet be replaced" +
|
||||
" with javascript in order to work."}
|
||||
|
||||
def __init__(self,db,uri='mongodb://127.0.0.1:5984/db',
|
||||
pool_size=0, folder=None, db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
|
||||
self.db = db
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args)
|
||||
import random
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
import pymongo.uri_parser
|
||||
|
||||
m = pymongo.uri_parser.parse_uri(uri)
|
||||
|
||||
self.SON = SON
|
||||
self.ObjectId = ObjectId
|
||||
self.random = random
|
||||
|
||||
self.dbengine = 'mongodb'
|
||||
self.folder = folder
|
||||
db['_lastsql'] = ''
|
||||
self.db_codec = 'UTF-8'
|
||||
self._after_connection = after_connection
|
||||
self.pool_size = pool_size
|
||||
#this is the minimum amount of replicates that it should wait
|
||||
# for on insert/update
|
||||
self.minimumreplication = adapter_args.get('minimumreplication',0)
|
||||
# by default all inserts and selects are performand asynchronous,
|
||||
# but now the default is
|
||||
# synchronous, except when overruled by either this default or
|
||||
# function parameter
|
||||
self.safe = adapter_args.get('safe',True)
|
||||
# load user setting for uploads in blob storage
|
||||
self.uploads_in_blob = adapter_args.get('uploads_in_blob', False)
|
||||
|
||||
if isinstance(m,tuple):
|
||||
m = {"database" : m[1]}
|
||||
if m.get('database') is None:
|
||||
raise SyntaxError("Database is required!")
|
||||
|
||||
def connector(uri=self.uri,m=m):
|
||||
# Connection() is deprecated
|
||||
if hasattr(self.driver, "MongoClient"):
|
||||
Connection = self.driver.MongoClient
|
||||
else:
|
||||
Connection = self.driver.Connection
|
||||
return Connection(uri)[m.get('database')]
|
||||
|
||||
self.reconnect(connector,cursor=False)
|
||||
|
||||
def object_id(self, arg=None):
|
||||
""" Convert input to a valid Mongodb ObjectId instance
|
||||
|
||||
self.object_id("<random>") -> ObjectId (not unique) instance """
|
||||
if not arg:
|
||||
arg = 0
|
||||
if isinstance(arg, basestring):
|
||||
# we assume an integer as default input
|
||||
rawhex = len(arg.replace("0x", "").replace("L", "")) == 24
|
||||
if arg.isdigit() and (not rawhex):
|
||||
arg = int(arg)
|
||||
elif arg == "<random>":
|
||||
arg = int("0x%sL" % \
|
||||
"".join([self.random.choice("0123456789abcdef") \
|
||||
for x in range(24)]), 0)
|
||||
elif arg.isalnum():
|
||||
if not arg.startswith("0x"):
|
||||
arg = "0x%s" % arg
|
||||
try:
|
||||
arg = int(arg, 0)
|
||||
except ValueError, e:
|
||||
raise ValueError(
|
||||
"invalid objectid argument string: %s" % e)
|
||||
else:
|
||||
raise ValueError("Invalid objectid argument string. " +
|
||||
"Requires an integer or base 16 value")
|
||||
elif isinstance(arg, self.ObjectId):
|
||||
return arg
|
||||
|
||||
if not isinstance(arg, (int, long)):
|
||||
raise TypeError("object_id argument must be of type " +
|
||||
"ObjectId or an objectid representable integer")
|
||||
hexvalue = hex(arg)[2:].rstrip('L').zfill(24)
|
||||
return self.ObjectId(hexvalue)
|
||||
|
||||
def parse_reference(self, value, field_type):
|
||||
# here we have to check for ObjectID before base parse
|
||||
if isinstance(value, self.ObjectId):
|
||||
value = long(str(value), 16)
|
||||
return super(MongoDBAdapter,
|
||||
self).parse_reference(value, field_type)
|
||||
|
||||
def parse_id(self, value, field_type):
|
||||
if isinstance(value, self.ObjectId):
|
||||
value = long(str(value), 16)
|
||||
return super(MongoDBAdapter,
|
||||
self).parse_id(value, field_type)
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
# the base adatpter does not support MongoDB ObjectId
|
||||
if isinstance(obj, self.ObjectId):
|
||||
value = obj
|
||||
else:
|
||||
value = NoSQLAdapter.represent(self, obj, fieldtype)
|
||||
# reference types must be convert to ObjectID
|
||||
if fieldtype =='date':
|
||||
if value is None:
|
||||
return value
|
||||
# this piece of data can be stripped off based on the fieldtype
|
||||
t = datetime.time(0, 0, 0)
|
||||
# mongodb doesn't has a date object and so it must datetime,
|
||||
# string or integer
|
||||
return datetime.datetime.combine(value, t)
|
||||
elif fieldtype == 'time':
|
||||
if value is None:
|
||||
return value
|
||||
# this piece of data can be stripped of based on the fieldtype
|
||||
d = datetime.date(2000, 1, 1)
|
||||
# mongodb doesn't has a time object and so it must datetime,
|
||||
# string or integer
|
||||
return datetime.datetime.combine(d, value)
|
||||
elif fieldtype == "blob":
|
||||
if value is None:
|
||||
return value
|
||||
from bson import Binary
|
||||
if not isinstance(value, Binary):
|
||||
if not isinstance(value, basestring):
|
||||
return Binary(str(value))
|
||||
return Binary(value)
|
||||
return value
|
||||
elif (isinstance(fieldtype, basestring) and
|
||||
fieldtype.startswith('list:')):
|
||||
if fieldtype.startswith('list:reference'):
|
||||
newval = []
|
||||
for v in value:
|
||||
newval.append(self.object_id(v))
|
||||
return newval
|
||||
return value
|
||||
elif ((isinstance(fieldtype, basestring) and
|
||||
fieldtype.startswith("reference")) or
|
||||
(isinstance(fieldtype, Table)) or fieldtype=="id"):
|
||||
value = self.object_id(value)
|
||||
return value
|
||||
|
||||
def create_table(self, table, migrate=True, fake_migrate=False,
|
||||
polymodel=None, isCapped=False):
|
||||
if isCapped:
|
||||
raise RuntimeError("Not implemented")
|
||||
|
||||
def count(self, query, distinct=None, snapshot=True):
|
||||
if distinct:
|
||||
raise RuntimeError("COUNT DISTINCT not supported")
|
||||
if not isinstance(query,Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
tablename = self.get_table(query)
|
||||
return long(self.select(query,[self.db[tablename]._id], {},
|
||||
count=True,snapshot=snapshot)['count'])
|
||||
# Maybe it would be faster if we just implemented the pymongo
|
||||
# .count() function which is probably quicker?
|
||||
# therefor call __select() connection[table].find(query).count()
|
||||
# Since this will probably reduce the return set?
|
||||
|
||||
def expand(self, expression, field_type=None):
|
||||
if isinstance(expression, Query):
|
||||
# any query using 'id':=
|
||||
# set name as _id (as per pymongo/mongodb primary key)
|
||||
# convert second arg to an objectid field
|
||||
# (if its not already)
|
||||
# if second arg is 0 convert to objectid
|
||||
if isinstance(expression.first,Field) and \
|
||||
((expression.first.type == 'id') or \
|
||||
("reference" in expression.first.type)):
|
||||
if expression.first.type == 'id':
|
||||
expression.first.name = '_id'
|
||||
# cast to Mongo ObjectId
|
||||
if isinstance(expression.second, (tuple, list, set)):
|
||||
expression.second = [self.object_id(item) for
|
||||
item in expression.second]
|
||||
else:
|
||||
expression.second = self.object_id(expression.second)
|
||||
result = expression.op(expression.first, expression.second)
|
||||
|
||||
if isinstance(expression, Field):
|
||||
if expression.type=='id':
|
||||
result = "_id"
|
||||
else:
|
||||
result = expression.name
|
||||
elif isinstance(expression, (Expression, Query)):
|
||||
if not expression.second is None:
|
||||
result = expression.op(expression.first, expression.second)
|
||||
elif not expression.first is None:
|
||||
result = expression.op(expression.first)
|
||||
elif not isinstance(expression.op, str):
|
||||
result = expression.op()
|
||||
else:
|
||||
result = expression.op
|
||||
elif field_type:
|
||||
result = self.represent(expression,field_type)
|
||||
elif isinstance(expression,(list,tuple)):
|
||||
result = [self.represent(item,field_type) for
|
||||
item in expression]
|
||||
else:
|
||||
result = expression
|
||||
return result
|
||||
|
||||
def drop(self, table, mode=''):
|
||||
ctable = self.connection[table._tablename]
|
||||
ctable.drop()
|
||||
|
||||
def truncate(self, table, mode, safe=None):
|
||||
if safe == None:
|
||||
safe=self.safe
|
||||
ctable = self.connection[table._tablename]
|
||||
ctable.remove(None, safe=True)
|
||||
|
||||
def select(self, query, fields, attributes, count=False,
|
||||
snapshot=False):
|
||||
mongofields_dict = self.SON()
|
||||
mongoqry_dict = {}
|
||||
new_fields, mongosort_list = [], []
|
||||
# try an orderby attribute
|
||||
orderby = attributes.get('orderby', False)
|
||||
limitby = attributes.get('limitby', False)
|
||||
# distinct = attributes.get('distinct', False)
|
||||
if 'for_update' in attributes:
|
||||
logging.warn('mongodb does not support for_update')
|
||||
for key in set(attributes.keys())-set(('limitby',
|
||||
'orderby','for_update')):
|
||||
if attributes[key] is not None:
|
||||
logging.warn('select attribute not implemented: %s' % key)
|
||||
if limitby:
|
||||
limitby_skip, limitby_limit = limitby[0], int(limitby[1])
|
||||
else:
|
||||
limitby_skip = limitby_limit = 0
|
||||
if orderby:
|
||||
if isinstance(orderby, (list, tuple)):
|
||||
orderby = xorify(orderby)
|
||||
# !!!! need to add 'random'
|
||||
for f in self.expand(orderby).split(','):
|
||||
if f.startswith('-'):
|
||||
mongosort_list.append((f[1:], -1))
|
||||
else:
|
||||
mongosort_list.append((f, 1))
|
||||
for item in fields:
|
||||
if isinstance(item, SQLALL):
|
||||
new_fields += item._table
|
||||
else:
|
||||
new_fields.append(item)
|
||||
fields = new_fields
|
||||
if isinstance(query,Query):
|
||||
tablename = self.get_table(query)
|
||||
elif len(fields) != 0:
|
||||
tablename = fields[0].tablename
|
||||
else:
|
||||
raise SyntaxError("The table name could not be found in " +
|
||||
"the query nor from the select statement.")
|
||||
mongoqry_dict = self.expand(query)
|
||||
fields = fields or self.db[tablename]
|
||||
for field in fields:
|
||||
mongofields_dict[field.name] = 1
|
||||
ctable = self.connection[tablename]
|
||||
if count:
|
||||
return {'count' : ctable.find(
|
||||
mongoqry_dict, mongofields_dict,
|
||||
skip=limitby_skip, limit=limitby_limit,
|
||||
sort=mongosort_list, snapshot=snapshot).count()}
|
||||
else:
|
||||
# pymongo cursor object
|
||||
mongo_list_dicts = ctable.find(mongoqry_dict,
|
||||
mongofields_dict, skip=limitby_skip,
|
||||
limit=limitby_limit, sort=mongosort_list,
|
||||
snapshot=snapshot)
|
||||
rows = []
|
||||
# populate row in proper order
|
||||
# Here we replace ._id with .id to follow the standard naming
|
||||
colnames = []
|
||||
newnames = []
|
||||
for field in fields:
|
||||
colname = str(field)
|
||||
colnames.append(colname)
|
||||
tablename, fieldname = colname.split(".")
|
||||
if fieldname == "_id":
|
||||
# Mongodb reserved uuid key
|
||||
field.name = "id"
|
||||
newnames.append(".".join((tablename, field.name)))
|
||||
|
||||
for record in mongo_list_dicts:
|
||||
row=[]
|
||||
for colname in colnames:
|
||||
tablename, fieldname = colname.split(".")
|
||||
# switch to Mongo _id uuids for retrieving
|
||||
# record id's
|
||||
if fieldname == "id": fieldname = "_id"
|
||||
if fieldname in record:
|
||||
value = record[fieldname]
|
||||
else:
|
||||
value = None
|
||||
row.append(value)
|
||||
rows.append(row)
|
||||
processor = attributes.get('processor', self.parse)
|
||||
result = processor(rows, fields, newnames, False)
|
||||
return result
|
||||
|
||||
def insert(self, table, fields, safe=None):
|
||||
"""Safe determines whether a asynchronous request is done or a
|
||||
synchronous action is done
|
||||
For safety, we use by default synchronous requests"""
|
||||
|
||||
values = dict()
|
||||
if safe is None:
|
||||
safe = self.safe
|
||||
ctable = self.connection[table._tablename]
|
||||
for k, v in fields:
|
||||
if not k.name in ["id", "safe"]:
|
||||
fieldname = k.name
|
||||
fieldtype = table[k.name].type
|
||||
values[fieldname] = self.represent(v, fieldtype)
|
||||
|
||||
ctable.insert(values, safe=safe)
|
||||
return long(str(values['_id']), 16)
|
||||
|
||||
def update(self, tablename, query, fields, safe=None):
|
||||
if safe == None:
|
||||
safe = self.safe
|
||||
# return amount of adjusted rows or zero, but no exceptions
|
||||
# @ related not finding the result
|
||||
if not isinstance(query, Query):
|
||||
raise RuntimeError("Not implemented")
|
||||
amount = self.count(query, False)
|
||||
if not isinstance(query, Query):
|
||||
raise SyntaxError("Not Supported")
|
||||
filter = None
|
||||
if query:
|
||||
filter = self.expand(query)
|
||||
# do not try to update id fields to avoid backend errors
|
||||
modify = {'$set': dict((k.name, self.represent(v, k.type)) for
|
||||
k, v in fields if (not k.name in ("_id", "id")))}
|
||||
try:
|
||||
result = self.connection[tablename].update(filter,
|
||||
modify, multi=True, safe=safe)
|
||||
if safe:
|
||||
try:
|
||||
# if result count is available fetch it
|
||||
return result["n"]
|
||||
except (KeyError, AttributeError, TypeError):
|
||||
return amount
|
||||
else:
|
||||
return amount
|
||||
except Exception, e:
|
||||
# TODO Reverse update query to verifiy that the query succeded
|
||||
raise RuntimeError("uncaught exception when updating rows: %s" % e)
|
||||
|
||||
def delete(self, tablename, query, safe=None):
|
||||
if safe is None:
|
||||
safe = self.safe
|
||||
amount = 0
|
||||
amount = self.count(query, False)
|
||||
if not isinstance(query, Query):
|
||||
raise RuntimeError("query type %s is not supported" % \
|
||||
type(query))
|
||||
filter = self.expand(query)
|
||||
self.connection[tablename].remove(filter, safe=safe)
|
||||
return amount
|
||||
|
||||
def bulk_insert(self, table, items):
|
||||
return [self.insert(table,item) for item in items]
|
||||
|
||||
## OPERATORS
|
||||
def INVERT(self, first):
|
||||
#print "in invert first=%s" % first
|
||||
return '-%s' % self.expand(first)
|
||||
|
||||
# TODO This will probably not work:(
|
||||
def NOT(self, first):
|
||||
return {'$not': self.expand(first)}
|
||||
|
||||
def AND(self,first,second):
|
||||
# pymongo expects: .find({'$and': [{'x':'1'}, {'y':'2'}]})
|
||||
return {'$and': [self.expand(first),self.expand(second)]}
|
||||
|
||||
def OR(self,first,second):
|
||||
# pymongo expects: .find({'$or': [{'name':'1'}, {'name':'2'}]})
|
||||
return {'$or': [self.expand(first),self.expand(second)]}
|
||||
|
||||
def BELONGS(self, first, second):
|
||||
if isinstance(second, str):
|
||||
return {self.expand(first) : {"$in" : [ second[:-1]]} }
|
||||
elif second==[] or second==() or second==set():
|
||||
return {1:0}
|
||||
items = [self.expand(item, first.type) for item in second]
|
||||
return {self.expand(first) : {"$in" : items} }
|
||||
|
||||
def EQ(self,first,second=None):
|
||||
result = {}
|
||||
result[self.expand(first)] = self.expand(second)
|
||||
return result
|
||||
|
||||
def NE(self, first, second=None):
|
||||
result = {}
|
||||
result[self.expand(first)] = {'$ne': self.expand(second)}
|
||||
return result
|
||||
|
||||
def LT(self,first,second=None):
|
||||
if second is None:
|
||||
raise RuntimeError("Cannot compare %s < None" % first)
|
||||
result = {}
|
||||
result[self.expand(first)] = {'$lt': self.expand(second)}
|
||||
return result
|
||||
|
||||
def LE(self,first,second=None):
|
||||
if second is None:
|
||||
raise RuntimeError("Cannot compare %s <= None" % first)
|
||||
result = {}
|
||||
result[self.expand(first)] = {'$lte': self.expand(second)}
|
||||
return result
|
||||
|
||||
def GT(self,first,second):
|
||||
result = {}
|
||||
result[self.expand(first)] = {'$gt': self.expand(second)}
|
||||
return result
|
||||
|
||||
def GE(self,first,second=None):
|
||||
if second is None:
|
||||
raise RuntimeError("Cannot compare %s >= None" % first)
|
||||
result = {}
|
||||
result[self.expand(first)] = {'$gte': self.expand(second)}
|
||||
return result
|
||||
|
||||
def ADD(self, first, second):
|
||||
raise NotImplementedError(self.error_messages["javascript_needed"])
|
||||
return '%s + %s' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def SUB(self, first, second):
|
||||
raise NotImplementedError(self.error_messages["javascript_needed"])
|
||||
return '(%s - %s)' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def MUL(self, first, second):
|
||||
raise NotImplementedError(self.error_messages["javascript_needed"])
|
||||
return '(%s * %s)' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def DIV(self, first, second):
|
||||
raise NotImplementedError(self.error_messages["javascript_needed"])
|
||||
return '(%s / %s)' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def MOD(self, first, second):
|
||||
raise NotImplementedError(self.error_messages["javascript_needed"])
|
||||
return '(%s %% %s)' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def AS(self, first, second):
|
||||
raise NotImplementedError(self.error_messages["javascript_needed"])
|
||||
return '%s AS %s' % (self.expand(first), second)
|
||||
|
||||
# We could implement an option that simulates a full featured SQL
|
||||
# database. But I think the option should be set explicit or
|
||||
# implemented as another library.
|
||||
def ON(self, first, second):
|
||||
raise NotImplementedError("This is not possible in NoSQL" +
|
||||
" but can be simulated with a wrapper.")
|
||||
return '%s ON %s' % (self.expand(first), self.expand(second))
|
||||
|
||||
# BLOW ARE TWO IMPLEMENTATIONS OF THE SAME FUNCITONS
|
||||
# WHICH ONE IS BEST?
|
||||
|
||||
def COMMA(self, first, second):
|
||||
return '%s, %s' % (self.expand(first), self.expand(second))
|
||||
|
||||
def LIKE(self, first, second):
|
||||
#escaping regex operators?
|
||||
return {self.expand(first): ('%s' % \
|
||||
self.expand(second, 'string').replace('%','/'))}
|
||||
|
||||
def ILIKE(self, first, second):
|
||||
val = second if isinstance(second,self.ObjectId) else {
|
||||
'$regex': second.replace('%', ''), '$options': 'i'}
|
||||
return {self.expand(first): val}
|
||||
|
||||
def STARTSWITH(self, first, second):
|
||||
#escaping regex operators?
|
||||
return {self.expand(first): ('/^%s/' % \
|
||||
self.expand(second, 'string'))}
|
||||
|
||||
def ENDSWITH(self, first, second):
|
||||
#escaping regex operators?
|
||||
return {self.expand(first): ('/%s^/' % \
|
||||
self.expand(second, 'string'))}
|
||||
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
# silently ignore, only case sensitive
|
||||
# There is a technical difference, but mongodb doesn't support
|
||||
# that, but the result will be the same
|
||||
val = second if isinstance(second,self.ObjectId) else \
|
||||
{'$regex':".*" + re.escape(self.expand(second, 'string')) + ".*"}
|
||||
return {self.expand(first) : val}
|
||||
|
||||
def LIKE(self, first, second):
|
||||
import re
|
||||
return {self.expand(first): {'$regex': \
|
||||
re.escape(self.expand(second,
|
||||
'string')).replace('%','.*')}}
|
||||
|
||||
#TODO verify full compatibilty with official SQL Like operator
|
||||
def STARTSWITH(self, first, second):
|
||||
#TODO Solve almost the same problem as with endswith
|
||||
import re
|
||||
return {self.expand(first): {'$regex' : '^' +
|
||||
re.escape(self.expand(second,
|
||||
'string'))}}
|
||||
|
||||
#TODO verify full compatibilty with official SQL Like operator
|
||||
def ENDSWITH(self, first, second):
|
||||
#escaping regex operators?
|
||||
#TODO if searched for a name like zsa_corbitt and the function
|
||||
# is endswith('a') then this is also returned.
|
||||
# Aldo it end with a t
|
||||
import re
|
||||
return {self.expand(first): {'$regex': \
|
||||
re.escape(self.expand(second, 'string')) + '$'}}
|
||||
|
||||
#TODO verify full compatibilty with official oracle contains operator
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
# silently ignore, only case sensitive
|
||||
#There is a technical difference, but mongodb doesn't support
|
||||
# that, but the result will be the same
|
||||
#TODO contains operators need to be transformed to Regex
|
||||
return {self.expand(first) : {'$regex': \
|
||||
".*" + re.escape(self.expand(second, 'string')) + ".*"}}
|
||||
|
||||
@@ -1,513 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from .._globals import IDENTITY, LOGGER
|
||||
from ..helpers.methods import varquote_aux
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class MSSQLAdapter(BaseAdapter):
|
||||
drivers = ('pyodbc',)
|
||||
T_SEP = 'T'
|
||||
|
||||
QUOTE_TEMPLATE = '"%s"'
|
||||
|
||||
types = {
|
||||
'boolean': 'BIT',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'TEXT',
|
||||
'json': 'TEXT',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'IMAGE',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATETIME',
|
||||
'time': 'CHAR(8)',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'INT IDENTITY PRIMARY KEY',
|
||||
'reference': 'INT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'TEXT',
|
||||
'list:string': 'TEXT',
|
||||
'list:reference': 'TEXT',
|
||||
'geometry': 'geometry',
|
||||
'geography': 'geography',
|
||||
'big-id': 'BIGINT IDENTITY PRIMARY KEY',
|
||||
'big-reference': 'BIGINT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def concat_add(self,tablename):
|
||||
return '; ALTER TABLE %s ADD ' % tablename
|
||||
|
||||
def varquote(self,name):
|
||||
return varquote_aux(name,'[%s]')
|
||||
|
||||
def EXTRACT(self,field,what):
|
||||
return "DATEPART(%s,%s)" % (what, self.expand(field))
|
||||
|
||||
def LEFT_JOIN(self):
|
||||
return 'LEFT OUTER JOIN'
|
||||
|
||||
def RANDOM(self):
|
||||
return 'NEWID()'
|
||||
|
||||
def ALLOW_NULL(self):
|
||||
return ' NULL'
|
||||
|
||||
def CAST(self, first, second):
|
||||
return first # apparently no cast necessary in MSSQL
|
||||
|
||||
def SUBSTRING(self,field,parameters):
|
||||
return 'SUBSTRING(%s,%s,%s)' % (self.expand(field), parameters[0], parameters[1])
|
||||
|
||||
def PRIMARY_KEY(self,key):
|
||||
return 'PRIMARY KEY CLUSTERED (%s)' % key
|
||||
|
||||
def AGGREGATE(self, first, what):
|
||||
if what == 'LENGTH':
|
||||
what = 'LEN'
|
||||
return "%s(%s)" % (what, self.expand(first))
|
||||
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
sql_s += ' TOP %i' % lmax
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
TRUE = 1
|
||||
FALSE = 0
|
||||
|
||||
REGEX_DSN = re.compile('^(?P<dsn>.+)$')
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:/]+)(\:(?P<port>[0-9]+))?/(?P<db>[^\?]+)(\?(?P<urlargs>.*))?$')
|
||||
REGEX_ARGPATTERN = re.compile('(?P<argkey>[^=]+)=(?P<argvalue>[^&]*)')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, srid=4326,
|
||||
after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "mssql"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.srid = srid
|
||||
self.find_or_make_work_folder()
|
||||
# ## read: http://bytes.com/groups/python/460325-cx_oracle-utf8
|
||||
ruri = uri.split('://',1)[1]
|
||||
if '@' not in ruri:
|
||||
try:
|
||||
m = self.REGEX_DSN.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
'Parsing uri string(%s) has no result' % self.uri)
|
||||
dsn = m.group('dsn')
|
||||
if not dsn:
|
||||
raise SyntaxError('DSN required')
|
||||
except SyntaxError:
|
||||
e = sys.exc_info()[1]
|
||||
LOGGER.error('NdGpatch error')
|
||||
raise e
|
||||
# was cnxn = 'DSN=%s' % dsn
|
||||
cnxn = dsn
|
||||
else:
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
"Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
port = m.group('port') or '1433'
|
||||
# Parse the optional url name-value arg pairs after the '?'
|
||||
# (in the form of arg1=value1&arg2=value2&...)
|
||||
# Default values (drivers like FreeTDS insist on uppercase parameter keys)
|
||||
argsdict = { 'DRIVER':'{SQL Server}' }
|
||||
urlargs = m.group('urlargs') or ''
|
||||
for argmatch in self.REGEX_ARGPATTERN.finditer(urlargs):
|
||||
argsdict[str(argmatch.group('argkey')).upper()] = argmatch.group('argvalue')
|
||||
urlargs = ';'.join(['%s=%s' % (ak, av) for (ak, av) in argsdict.iteritems()])
|
||||
cnxn = 'SERVER=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s;%s' \
|
||||
% (host, port, db, user, password, urlargs)
|
||||
def connector(cnxn=cnxn,driver_args=driver_args):
|
||||
return self.driver.connect(cnxn,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def lastrowid(self,table):
|
||||
#self.execute('SELECT @@IDENTITY;')
|
||||
self.execute('SELECT SCOPE_IDENTITY();')
|
||||
return long(self.cursor.fetchone()[0])
|
||||
|
||||
def rowslice(self,rows,minimum=0,maximum=None):
|
||||
if maximum is None:
|
||||
return rows[minimum:]
|
||||
return rows[minimum:maximum]
|
||||
|
||||
def EPOCH(self, first):
|
||||
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
|
||||
|
||||
def CONCAT(self, *items):
|
||||
return '(%s)' % ' + '.join(self.expand(x,'string') for x in items)
|
||||
|
||||
# GIS Spatial Extensions
|
||||
|
||||
# No STAsGeoJSON in MSSQL
|
||||
|
||||
def ST_ASTEXT(self, first):
|
||||
return '%s.STAsText()' %(self.expand(first))
|
||||
|
||||
def ST_CONTAINS(self, first, second):
|
||||
return '%s.STContains(%s)=1' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_DISTANCE(self, first, second):
|
||||
return '%s.STDistance(%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_EQUALS(self, first, second):
|
||||
return '%s.STEquals(%s)=1' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_INTERSECTS(self, first, second):
|
||||
return '%s.STIntersects(%s)=1' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_OVERLAPS(self, first, second):
|
||||
return '%s.STOverlaps(%s)=1' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
# no STSimplify in MSSQL
|
||||
|
||||
def ST_TOUCHES(self, first, second):
|
||||
return '%s.STTouches(%s)=1' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_WITHIN(self, first, second):
|
||||
return '%s.STWithin(%s)=1' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
field_is_type = fieldtype.startswith
|
||||
if field_is_type('geometry'):
|
||||
srid = 0 # MS SQL default srid for geometry
|
||||
geotype, parms = fieldtype[:-1].split('(')
|
||||
if parms:
|
||||
srid = parms
|
||||
return "geometry::STGeomFromText('%s',%s)" %(obj, srid)
|
||||
elif fieldtype == 'geography':
|
||||
srid = 4326 # MS SQL default srid for geography
|
||||
geotype, parms = fieldtype[:-1].split('(')
|
||||
if parms:
|
||||
srid = parms
|
||||
return "geography::STGeomFromText('%s',%s)" %(obj, srid)
|
||||
# else:
|
||||
# raise SyntaxError('Invalid field type %s' %fieldtype)
|
||||
return "geometry::STGeomFromText('%s',%s)" %(obj, srid)
|
||||
return BaseAdapter.represent(self, obj, fieldtype)
|
||||
|
||||
|
||||
class MSSQL3Adapter(MSSQLAdapter):
|
||||
"""Experimental support for pagination in MSSQL
|
||||
|
||||
Requires MSSQL >= 2005, uses `ROW_NUMBER()`
|
||||
"""
|
||||
|
||||
types = {
|
||||
'boolean': 'BIT',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'VARCHAR(MAX)',
|
||||
'json': 'VARCHAR(MAX)',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'IMAGE',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATETIME',
|
||||
'time': 'TIME(7)',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'INT IDENTITY PRIMARY KEY',
|
||||
'reference': 'INT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'VARCHAR(MAX)',
|
||||
'list:string': 'VARCHAR(MAX)',
|
||||
'list:reference': 'VARCHAR(MAX)',
|
||||
'geometry': 'geometry',
|
||||
'geography': 'geography',
|
||||
'big-id': 'BIGINT IDENTITY PRIMARY KEY',
|
||||
'big-reference': 'BIGINT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
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)
|
||||
def rowslice(self,rows,minimum=0,maximum=None):
|
||||
return rows
|
||||
|
||||
|
||||
class MSSQL4Adapter(MSSQLAdapter):
|
||||
"""Support for "native" pagination
|
||||
|
||||
Requires MSSQL >= 2012, uses `OFFSET ... ROWS ... FETCH NEXT ... ROWS ONLY`
|
||||
"""
|
||||
|
||||
types = {
|
||||
'boolean': 'BIT',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'VARCHAR(MAX)',
|
||||
'json': 'VARCHAR(MAX)',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'IMAGE',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATETIME',
|
||||
'time': 'TIME(7)',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'INT IDENTITY PRIMARY KEY',
|
||||
'reference': 'INT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'VARCHAR(MAX)',
|
||||
'list:string': 'VARCHAR(MAX)',
|
||||
'list:reference': 'VARCHAR(MAX)',
|
||||
'geometry': 'geometry',
|
||||
'geography': 'geography',
|
||||
'big-id': 'BIGINT IDENTITY PRIMARY KEY',
|
||||
'big-reference': 'BIGINT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
if lmin == 0:
|
||||
#top is still slightly faster, especially because
|
||||
#web2py's default to fetch references is to not specify
|
||||
#an orderby clause
|
||||
sql_s += ' TOP %i' % lmax
|
||||
else:
|
||||
if not sql_o:
|
||||
#if there is no orderby, we can't use the brand new statements
|
||||
#that being said, developer chose its own poison, so be it random
|
||||
sql_o += ' ORDER BY %s' % self.RANDOM()
|
||||
sql_o += ' OFFSET %i ROWS FETCH NEXT %i ROWS ONLY' % (lmin, lmax - lmin)
|
||||
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):
|
||||
return rows
|
||||
|
||||
|
||||
class MSSQL2Adapter(MSSQLAdapter):
|
||||
drivers = ('pyodbc',)
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'NVARCHAR(%(length)s)',
|
||||
'text': 'NTEXT',
|
||||
'json': 'NTEXT',
|
||||
'password': 'NVARCHAR(%(length)s)',
|
||||
'blob': 'IMAGE',
|
||||
'upload': 'NVARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATETIME',
|
||||
'time': 'CHAR(8)',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'INT IDENTITY PRIMARY KEY',
|
||||
'reference': 'INT, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'NTEXT',
|
||||
'list:string': 'NTEXT',
|
||||
'list:reference': 'NTEXT',
|
||||
'big-id': 'BIGINT IDENTITY PRIMARY KEY',
|
||||
'big-reference': 'BIGINT, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
value = BaseAdapter.represent(self, obj, fieldtype)
|
||||
if fieldtype in ('string','text', 'json') and value[:1]=="'":
|
||||
value = 'N'+value
|
||||
return value
|
||||
|
||||
def execute(self,a):
|
||||
return self.log_execute(a.decode('utf8'))
|
||||
|
||||
|
||||
class VerticaAdapter(MSSQLAdapter):
|
||||
drivers = ('pyodbc',)
|
||||
T_SEP = ' '
|
||||
|
||||
types = {
|
||||
'boolean': 'BOOLEAN',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'BYTEA',
|
||||
'json': 'VARCHAR(%(length)s)',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BYTEA',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'DOUBLE PRECISION',
|
||||
'decimal': 'DECIMAL(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'IDENTITY',
|
||||
'reference': 'INT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'BYTEA',
|
||||
'list:string': 'BYTEA',
|
||||
'list:reference': 'BYTEA',
|
||||
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
|
||||
def EXTRACT(self, first, what):
|
||||
return "DATE_PART('%s', TIMESTAMP %s)" % (what, self.expand(first))
|
||||
|
||||
def _truncate(self, table, mode=''):
|
||||
tablename = table._tablename
|
||||
return ['TRUNCATE %s %s;' % (tablename, mode or '')]
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
sql_o += ' LIMIT %i OFFSET %i' % (lmax - lmin, lmin)
|
||||
return 'SELECT %s %s FROM %s%s%s;' % \
|
||||
(sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def lastrowid(self,table):
|
||||
self.execute('SELECT LAST_INSERT_ID();')
|
||||
return long(self.cursor.fetchone()[0])
|
||||
|
||||
def execute(self, a):
|
||||
return self.log_execute(a)
|
||||
|
||||
|
||||
class SybaseAdapter(MSSQLAdapter):
|
||||
drivers = ('Sybase',)
|
||||
|
||||
types = {
|
||||
'boolean': 'BIT',
|
||||
'string': 'CHAR VARYING(%(length)s)',
|
||||
'text': 'TEXT',
|
||||
'json': 'TEXT',
|
||||
'password': 'CHAR VARYING(%(length)s)',
|
||||
'blob': 'IMAGE',
|
||||
'upload': 'CHAR VARYING(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATETIME',
|
||||
'time': 'CHAR(8)',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'INT IDENTITY PRIMARY KEY',
|
||||
'reference': 'INT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'TEXT',
|
||||
'list:string': 'TEXT',
|
||||
'list:reference': 'TEXT',
|
||||
'geometry': 'geometry',
|
||||
'geography': 'geography',
|
||||
'big-id': 'BIGINT IDENTITY PRIMARY KEY',
|
||||
'big-reference': 'BIGINT NULL, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, srid=4326,
|
||||
after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "sybase"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.srid = srid
|
||||
self.find_or_make_work_folder()
|
||||
# ## read: http://bytes.com/groups/python/460325-cx_oracle-utf8
|
||||
ruri = uri.split('://',1)[1]
|
||||
if '@' not in ruri:
|
||||
try:
|
||||
m = self.REGEX_DSN.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
'Parsing uri string(%s) has no result' % self.uri)
|
||||
dsn = m.group('dsn')
|
||||
if not dsn:
|
||||
raise SyntaxError('DSN required')
|
||||
except SyntaxError:
|
||||
e = sys.exc_info()[1]
|
||||
LOGGER.error('NdGpatch error')
|
||||
raise e
|
||||
else:
|
||||
m = self.REGEX_URI.match(uri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
"Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
port = m.group('port') or '1433'
|
||||
|
||||
dsn = 'sybase:host=%s:%s;dbname=%s' % (host,port,db)
|
||||
|
||||
driver_args.update(user = credential_decoder(user),
|
||||
password = credential_decoder(password))
|
||||
|
||||
def connector(dsn=dsn,driver_args=driver_args):
|
||||
return self.driver.connect(dsn,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from ..helpers.methods import varquote_aux
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class MySQLAdapter(BaseAdapter):
|
||||
drivers = ('MySQLdb','pymysql', 'mysqlconnector')
|
||||
|
||||
commit_on_alter_table = True
|
||||
support_distributed_transaction = True
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'LONGTEXT',
|
||||
'json': 'LONGTEXT',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'LONGBLOB',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'DOUBLE',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'DATETIME',
|
||||
'id': 'INT AUTO_INCREMENT NOT NULL',
|
||||
'reference': 'INT, INDEX %(index_name)s (%(field_name)s), FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'LONGTEXT',
|
||||
'list:string': 'LONGTEXT',
|
||||
'list:reference': 'LONGTEXT',
|
||||
'big-id': 'BIGINT AUTO_INCREMENT NOT NULL',
|
||||
'big-reference': 'BIGINT, INDEX %(index_name)s (%(field_name)s), FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT `FK_%(constraint_name)s` FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
QUOTE_TEMPLATE = "`%s`"
|
||||
|
||||
def varquote(self,name):
|
||||
return varquote_aux(name,'`%s`')
|
||||
|
||||
def RANDOM(self):
|
||||
return 'RAND()'
|
||||
|
||||
def SUBSTRING(self,field,parameters):
|
||||
return 'SUBSTRING(%s,%s,%s)' % (self.expand(field),
|
||||
parameters[0], parameters[1])
|
||||
|
||||
def EPOCH(self, first):
|
||||
return "UNIX_TIMESTAMP(%s)" % self.expand(first)
|
||||
|
||||
def CONCAT(self, *items):
|
||||
return 'CONCAT(%s)' % ','.join(self.expand(x,'string') for x in items)
|
||||
|
||||
def REGEXP(self,first,second):
|
||||
return '(%s REGEXP %s)' % (self.expand(first),
|
||||
self.expand(second,'string'))
|
||||
|
||||
def CAST(self, first, second):
|
||||
if second=='LONGTEXT': second = 'CHAR'
|
||||
return 'CAST(%s AS %s)' % (first, second)
|
||||
|
||||
def _drop(self,table,mode):
|
||||
# breaks db integrity but without this mysql does not drop table
|
||||
table_rname = table.sqlsafe
|
||||
return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table_rname,
|
||||
'SET FOREIGN_KEY_CHECKS=1;']
|
||||
|
||||
def _insert_empty(self, table):
|
||||
return 'INSERT INTO %s VALUES (DEFAULT);' % (table.sqlsafe)
|
||||
|
||||
def distributed_transaction_begin(self,key):
|
||||
self.execute('XA START;')
|
||||
|
||||
def prepare(self,key):
|
||||
self.execute("XA END;")
|
||||
self.execute("XA PREPARE;")
|
||||
|
||||
def commit_prepared(self,key):
|
||||
self.execute("XA COMMIT;")
|
||||
|
||||
def rollback_prepared(self,key):
|
||||
self.execute("XA ROLLBACK;")
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:/]+)(\:(?P<port>[0-9]+))?/(?P<db>[^?]+)(\?set_encoding=(?P<charset>\w+))?$')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "mysql"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError(
|
||||
"Invalid URI string in DAL: %s" % self.uri)
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
port = int(m.group('port') or '3306')
|
||||
charset = m.group('charset') or 'utf8'
|
||||
driver_args.update(db=db,
|
||||
user=credential_decoder(user),
|
||||
passwd=credential_decoder(password),
|
||||
host=host,
|
||||
port=port,
|
||||
charset=charset)
|
||||
|
||||
|
||||
def connector(driver_args=driver_args):
|
||||
return self.driver.connect(**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.execute('SET FOREIGN_KEY_CHECKS=1;')
|
||||
self.execute("SET sql_mode='NO_BACKSLASH_ESCAPES';")
|
||||
|
||||
def lastrowid(self,table):
|
||||
self.execute('select last_insert_id();')
|
||||
return int(self.cursor.fetchone()[0])
|
||||
@@ -1,191 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .._load import cx_Oracle
|
||||
from .base import BaseAdapter
|
||||
|
||||
class OracleAdapter(BaseAdapter):
|
||||
drivers = ('cx_Oracle',)
|
||||
|
||||
commit_on_alter_table = False
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR2(%(length)s)',
|
||||
'text': 'CLOB',
|
||||
'json': 'CLOB',
|
||||
'password': 'VARCHAR2(%(length)s)',
|
||||
'blob': 'CLOB',
|
||||
'upload': 'VARCHAR2(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'NUMBER',
|
||||
'float': 'FLOAT',
|
||||
'double': 'BINARY_DOUBLE',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'CHAR(8)',
|
||||
'datetime': 'DATE',
|
||||
'id': 'NUMBER PRIMARY KEY',
|
||||
'reference': 'NUMBER, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'CLOB',
|
||||
'list:string': 'CLOB',
|
||||
'list:reference': 'CLOB',
|
||||
'big-id': 'NUMBER PRIMARY KEY',
|
||||
'big-reference': 'NUMBER, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
|
||||
def trigger_name(self,tablename):
|
||||
return '%s_trigger' % tablename
|
||||
|
||||
def LEFT_JOIN(self):
|
||||
return 'LEFT OUTER JOIN'
|
||||
|
||||
def RANDOM(self):
|
||||
return 'dbms_random.value'
|
||||
|
||||
def NOT_NULL(self,default,field_type):
|
||||
return 'DEFAULT %s NOT NULL' % self.represent(default,field_type)
|
||||
|
||||
def REGEXP(self, first, second):
|
||||
return 'REGEXP_LIKE(%s, %s)' % (self.expand(first),
|
||||
self.expand(second, 'string'))
|
||||
|
||||
def _drop(self,table,mode):
|
||||
sequence_name = table._sequence_name
|
||||
return ['DROP TABLE %s %s;' % (table.sqlsafe, mode), 'DROP SEQUENCE %s;' % sequence_name]
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
if len(sql_w) > 1:
|
||||
sql_w_row = sql_w + ' AND w_row > %i' % lmin
|
||||
else:
|
||||
sql_w_row = 'WHERE w_row > %i' % lmin
|
||||
return 'SELECT %s %s FROM (SELECT w_tmp.*, ROWNUM w_row FROM (SELECT %s FROM %s%s%s) w_tmp WHERE ROWNUM<=%i) %s %s %s;' % (sql_s, sql_f, sql_f, sql_t, sql_w, sql_o, lmax, sql_t, sql_w_row, sql_o)
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def constraint_name(self, tablename, fieldname):
|
||||
constraint_name = BaseAdapter.constraint_name(self, tablename, fieldname)
|
||||
if len(constraint_name)>30:
|
||||
constraint_name = '%s_%s__constraint' % (tablename[:10], fieldname[:7])
|
||||
return constraint_name
|
||||
|
||||
def represent_exceptions(self, obj, fieldtype):
|
||||
if fieldtype == 'blob':
|
||||
obj = base64.b64encode(str(obj))
|
||||
return ":CLOB('%s')" % obj
|
||||
elif fieldtype == 'date':
|
||||
if isinstance(obj, (datetime.date, datetime.datetime)):
|
||||
obj = obj.isoformat()[:10]
|
||||
else:
|
||||
obj = str(obj)
|
||||
return "to_date('%s','yyyy-mm-dd')" % obj
|
||||
elif fieldtype == 'datetime':
|
||||
if isinstance(obj, datetime.datetime):
|
||||
obj = obj.isoformat()[:19].replace('T',' ')
|
||||
elif isinstance(obj, datetime.date):
|
||||
obj = obj.isoformat()[:10]+' 00:00:00'
|
||||
else:
|
||||
obj = str(obj)
|
||||
return "to_date('%s','yyyy-mm-dd hh24:mi:ss')" % obj
|
||||
return None
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "oracle"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
if not 'threaded' in driver_args:
|
||||
driver_args['threaded']=True
|
||||
def connector(uri=ruri,driver_args=driver_args):
|
||||
return self.driver.connect(uri,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';")
|
||||
self.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS';")
|
||||
|
||||
oracle_fix = re.compile("[^']*('[^']*'[^']*)*\:(?P<clob>CLOB\('([^']+|'')*'\))")
|
||||
|
||||
def execute(self, command, args=None):
|
||||
args = args or []
|
||||
i = 1
|
||||
while True:
|
||||
m = self.oracle_fix.match(command)
|
||||
if not m:
|
||||
break
|
||||
command = command[:m.start('clob')] + str(i) + command[m.end('clob'):]
|
||||
args.append(m.group('clob')[6:-2].replace("''", "'"))
|
||||
i += 1
|
||||
if command[-1:]==';':
|
||||
command = command[:-1]
|
||||
return self.log_execute(command, args)
|
||||
|
||||
def create_sequence_and_triggers(self, query, table, **args):
|
||||
tablename = table._rname or table._tablename
|
||||
id_name = table._id.name
|
||||
sequence_name = table._sequence_name
|
||||
trigger_name = table._trigger_name
|
||||
self.execute(query)
|
||||
self.execute('CREATE SEQUENCE %s START WITH 1 INCREMENT BY 1 NOMAXVALUE MINVALUE -1;' % sequence_name)
|
||||
self.execute("""
|
||||
CREATE OR REPLACE TRIGGER %(trigger_name)s BEFORE INSERT ON %(tablename)s FOR EACH ROW
|
||||
DECLARE
|
||||
curr_val NUMBER;
|
||||
diff_val NUMBER;
|
||||
PRAGMA autonomous_transaction;
|
||||
BEGIN
|
||||
IF :NEW.%(id)s IS NOT NULL THEN
|
||||
EXECUTE IMMEDIATE 'SELECT %(sequence_name)s.nextval FROM dual' INTO curr_val;
|
||||
diff_val := :NEW.%(id)s - curr_val - 1;
|
||||
IF diff_val != 0 THEN
|
||||
EXECUTE IMMEDIATE 'alter sequence %(sequence_name)s increment by '|| diff_val;
|
||||
EXECUTE IMMEDIATE 'SELECT %(sequence_name)s.nextval FROM dual' INTO curr_val;
|
||||
EXECUTE IMMEDIATE 'alter sequence %(sequence_name)s increment by 1';
|
||||
END IF;
|
||||
END IF;
|
||||
SELECT %(sequence_name)s.nextval INTO :NEW.%(id)s FROM DUAL;
|
||||
END;
|
||||
""" % dict(trigger_name=trigger_name, tablename=tablename,
|
||||
sequence_name=sequence_name,id=id_name))
|
||||
|
||||
def lastrowid(self,table):
|
||||
sequence_name = table._sequence_name
|
||||
self.execute('SELECT %s.currval FROM dual;' % sequence_name)
|
||||
return long(self.cursor.fetchone()[0])
|
||||
|
||||
#def parse_value(self, value, field_type, blob_decode=True):
|
||||
# if blob_decode and isinstance(value, cx_Oracle.LOB):
|
||||
# try:
|
||||
# value = value.read()
|
||||
# except self.driver.ProgrammingError:
|
||||
# # After a subsequent fetch the LOB value is not valid anymore
|
||||
# pass
|
||||
# return BaseAdapter.parse_value(self, value, field_type, blob_decode)
|
||||
|
||||
def _fetchall(self):
|
||||
if any(x[1]==cx_Oracle.LOB for x in self.cursor.description):
|
||||
return [tuple([(c.read() if type(c) == cx_Oracle.LOB else c) \
|
||||
for c in r]) for r in self.cursor]
|
||||
else:
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def sqlsafe_table(self, tablename, ot=None):
|
||||
if ot is not None:
|
||||
return (self.QUOTE_TEMPLATE + ' ' \
|
||||
+ self.QUOTE_TEMPLATE) % (ot, tablename)
|
||||
return self.QUOTE_TEMPLATE % tablename
|
||||
@@ -1,420 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
|
||||
from .._load import psycopg2_adapt
|
||||
from .._globals import IDENTITY, LOGGER
|
||||
from ..helpers.methods import varquote_aux
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class PostgreSQLAdapter(BaseAdapter):
|
||||
drivers = ('psycopg2','pg8000')
|
||||
|
||||
QUOTE_TEMPLATE = '"%s"'
|
||||
|
||||
support_distributed_transaction = True
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'TEXT',
|
||||
'json': 'TEXT',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BYTEA',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INTEGER',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT8',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'TIMESTAMP',
|
||||
'id': 'SERIAL PRIMARY KEY',
|
||||
'reference': 'INTEGER REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'TEXT',
|
||||
'list:string': 'TEXT',
|
||||
'list:reference': 'TEXT',
|
||||
'geometry': 'GEOMETRY',
|
||||
'geography': 'GEOGRAPHY',
|
||||
'big-id': 'BIGSERIAL PRIMARY KEY',
|
||||
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference FK': ', CONSTRAINT "FK_%(constraint_name)s" FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'reference TFK': ' CONSTRAINT "FK_%(foreign_table)s_PK" FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
|
||||
|
||||
}
|
||||
|
||||
|
||||
def varquote(self,name):
|
||||
return varquote_aux(name,'"%s"')
|
||||
|
||||
def adapt(self,obj):
|
||||
if self.driver_name == 'psycopg2':
|
||||
return psycopg2_adapt(obj).getquoted()
|
||||
elif self.driver_name == 'pg8000':
|
||||
return "'%s'" % str(obj).replace("%","%%").replace("'","''")
|
||||
else:
|
||||
return "'%s'" % str(obj).replace("'","''")
|
||||
|
||||
def sequence_name(self,table):
|
||||
return self.QUOTE_TEMPLATE % (table + '_id_seq')
|
||||
|
||||
def RANDOM(self):
|
||||
return 'RANDOM()'
|
||||
|
||||
def ADD(self, first, second):
|
||||
t = first.type
|
||||
if t in ('text','string','password', 'json', 'upload','blob'):
|
||||
return '(%s || %s)' % (self.expand(first), self.expand(second, t))
|
||||
else:
|
||||
return '(%s + %s)' % (self.expand(first), self.expand(second, t))
|
||||
|
||||
def distributed_transaction_begin(self,key):
|
||||
return
|
||||
|
||||
def prepare(self,key):
|
||||
self.execute("PREPARE TRANSACTION '%s';" % key)
|
||||
|
||||
def commit_prepared(self,key):
|
||||
self.execute("COMMIT PREPARED '%s';" % key)
|
||||
|
||||
def rollback_prepared(self,key):
|
||||
self.execute("ROLLBACK PREPARED '%s';" % key)
|
||||
|
||||
def create_sequence_and_triggers(self, query, table, **args):
|
||||
# following lines should only be executed if table._sequence_name does not exist
|
||||
# self.execute('CREATE SEQUENCE %s;' % table._sequence_name)
|
||||
# self.execute("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT NEXTVAL('%s');" \
|
||||
# % (table._tablename, table._fieldname, table._sequence_name))
|
||||
self.execute(query)
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:@]+)(\:(?P<port>[0-9]+))?/(?P<db>[^\?]+)(\?sslmode=(?P<sslmode>.+))?$')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, srid=4326,
|
||||
after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "postgres"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.srid = srid
|
||||
self.find_or_make_work_folder()
|
||||
self._last_insert = None # for INSERT ... RETURNING ID
|
||||
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError("Invalid URI string in DAL")
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
port = m.group('port') or '5432'
|
||||
sslmode = m.group('sslmode')
|
||||
if sslmode:
|
||||
msg = ("dbname='%s' user='%s' host='%s' "
|
||||
"port=%s password='%s' sslmode='%s'") \
|
||||
% (db, user, host, port, password, sslmode)
|
||||
else:
|
||||
msg = ("dbname='%s' user='%s' host='%s' "
|
||||
"port=%s password='%s'") \
|
||||
% (db, user, host, port, password)
|
||||
# choose diver according uri
|
||||
if self.driver:
|
||||
self.__version__ = "%s %s" % (self.driver.__name__,
|
||||
self.driver.__version__)
|
||||
else:
|
||||
self.__version__ = None
|
||||
def connector(msg=msg,driver_args=driver_args):
|
||||
return self.driver.connect(msg,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.connection.set_client_encoding('UTF8')
|
||||
self.execute("SET standard_conforming_strings=on;")
|
||||
self.try_json()
|
||||
|
||||
def _insert(self, table, fields):
|
||||
table_rname = table.sqlsafe
|
||||
if fields:
|
||||
keys = ','.join(f.sqlsafe_name for f, v in fields)
|
||||
values = ','.join(self.expand(v, f.type) for f, v in fields)
|
||||
if table._id:
|
||||
self._last_insert = (table._id, 1)
|
||||
return 'INSERT INTO %s(%s) VALUES (%s) RETURNING %s;' % (
|
||||
table_rname, keys, values, table._id.name)
|
||||
else:
|
||||
self._last_insert = None
|
||||
return 'INSERT INTO %s(%s) VALUES (%s);' % (table_rname, keys, values)
|
||||
else:
|
||||
self._last_insert
|
||||
return self._insert_empty(table)
|
||||
|
||||
def lastrowid(self, table=None):
|
||||
if self._last_insert:
|
||||
return int(self.cursor.fetchone()[0])
|
||||
else:
|
||||
self.execute("select lastval()")
|
||||
return int(self.cursor.fetchone()[0])
|
||||
|
||||
def try_json(self):
|
||||
# check JSON data type support
|
||||
# (to be added to after_connection)
|
||||
|
||||
# until pg8000 supports json, leave this commented
|
||||
#if self.driver_name == "pg8000":
|
||||
# supports_json = self.connection.server_version >= "9.2.0"
|
||||
|
||||
if (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"
|
||||
if (self.driver_name == "psycopg2" and
|
||||
self.driver.__version__ >= '2.5.0'):
|
||||
self.driver_auto_json = ['loads']
|
||||
else:
|
||||
LOGGER.debug("Your database version does not support the JSON"
|
||||
" data type (using TEXT instead)")
|
||||
|
||||
def LIKE(self,first,second):
|
||||
args = (self.expand(first), self.expand(second,'string'))
|
||||
if not first.type in ('string', 'text', 'json'):
|
||||
return '(%s LIKE %s)' % (
|
||||
self.CAST(args[0], 'CHAR(%s)' % first.length), args[1])
|
||||
else:
|
||||
return '(%s LIKE %s)' % args
|
||||
|
||||
def ILIKE(self,first,second):
|
||||
args = (self.expand(first), self.expand(second,'string'))
|
||||
if not first.type in ('string', 'text', 'json'):
|
||||
return '(%s LIKE %s)' % (
|
||||
self.CAST(args[0], 'CHAR(%s)' % first.length), args[1])
|
||||
else:
|
||||
return '(%s ILIKE %s)' % args
|
||||
|
||||
def REGEXP(self,first,second):
|
||||
return '(%s ~ %s)' % (self.expand(first),
|
||||
self.expand(second,'string'))
|
||||
|
||||
# GIS functions
|
||||
|
||||
def ST_ASGEOJSON(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_AsGeoJSON.html
|
||||
"""
|
||||
return 'ST_AsGeoJSON(%s,%s,%s,%s)' %(second['version'],
|
||||
self.expand(first), second['precision'], second['options'])
|
||||
|
||||
def ST_ASTEXT(self, first):
|
||||
"""
|
||||
http://postgis.org/docs/ST_AsText.html
|
||||
"""
|
||||
return 'ST_AsText(%s)' %(self.expand(first))
|
||||
|
||||
def ST_X(self, first):
|
||||
"""
|
||||
http://postgis.org/docs/ST_X.html
|
||||
"""
|
||||
return 'ST_X(%s)' %(self.expand(first))
|
||||
|
||||
def ST_Y(self, first):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Y.html
|
||||
"""
|
||||
return 'ST_Y(%s)' %(self.expand(first))
|
||||
|
||||
def ST_CONTAINS(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Contains.html
|
||||
"""
|
||||
return 'ST_Contains(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_DISTANCE(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Distance.html
|
||||
"""
|
||||
return 'ST_Distance(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_EQUALS(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Equals.html
|
||||
"""
|
||||
return 'ST_Equals(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_INTERSECTS(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Intersects.html
|
||||
"""
|
||||
return 'ST_Intersects(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_OVERLAPS(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Overlaps.html
|
||||
"""
|
||||
return 'ST_Overlaps(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_SIMPLIFY(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Simplify.html
|
||||
"""
|
||||
return 'ST_Simplify(%s,%s)' %(self.expand(first), self.expand(second, 'double'))
|
||||
|
||||
def ST_TOUCHES(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Touches.html
|
||||
"""
|
||||
return 'ST_Touches(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_WITHIN(self, first, second):
|
||||
"""
|
||||
http://postgis.org/docs/ST_Within.html
|
||||
"""
|
||||
return 'ST_Within(%s,%s)' %(self.expand(first), self.expand(second, first.type))
|
||||
|
||||
def ST_DWITHIN(self, first, (second, third)):
|
||||
"""
|
||||
http://postgis.org/docs/ST_DWithin.html
|
||||
"""
|
||||
return 'ST_DWithin(%s,%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type),
|
||||
self.expand(third, 'double'))
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
field_is_type = fieldtype.startswith
|
||||
if field_is_type('geo'):
|
||||
srid = 4326 # postGIS default srid for geometry
|
||||
geotype, parms = fieldtype[:-1].split('(')
|
||||
parms = parms.split(',')
|
||||
if len(parms) >= 2:
|
||||
schema, srid = parms[:2]
|
||||
if field_is_type('geometry'):
|
||||
value = "ST_GeomFromText('%s',%s)" %(obj, srid)
|
||||
elif field_is_type('geography'):
|
||||
value = "ST_GeogFromText('SRID=%s;%s')" %(srid, obj)
|
||||
# else:
|
||||
# raise SyntaxError('Invalid field type %s' %fieldtype)
|
||||
return value
|
||||
return BaseAdapter.represent(self, obj, fieldtype)
|
||||
|
||||
def _drop(self, table, mode='restrict'):
|
||||
if mode not in ['restrict', 'cascade', '']:
|
||||
raise ValueError('Invalid mode: %s' % mode)
|
||||
return ['DROP TABLE ' + table.sqlsafe + ' ' + str(mode) + ';']
|
||||
|
||||
class NewPostgreSQLAdapter(PostgreSQLAdapter):
|
||||
drivers = ('psycopg2','pg8000')
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'TEXT',
|
||||
'json': 'TEXT',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BYTEA',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INTEGER',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'FLOAT8',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'TIMESTAMP',
|
||||
'id': 'SERIAL PRIMARY KEY',
|
||||
'reference': 'INTEGER REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'BIGINT[]',
|
||||
'list:string': 'TEXT[]',
|
||||
'list:reference': 'BIGINT[]',
|
||||
'geometry': 'GEOMETRY',
|
||||
'geography': 'GEOGRAPHY',
|
||||
'big-id': 'BIGSERIAL PRIMARY KEY',
|
||||
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def parse_list_integers(self, value, field_type):
|
||||
return value
|
||||
|
||||
def parse_list_references(self, value, field_type):
|
||||
return [self.parse_reference(r, field_type[5:]) for r in value]
|
||||
|
||||
def parse_list_strings(self, value, field_type):
|
||||
return value
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
field_is_type = fieldtype.startswith
|
||||
if field_is_type('list:'):
|
||||
if not obj:
|
||||
obj = []
|
||||
elif not isinstance(obj, (list, tuple)):
|
||||
obj = [obj]
|
||||
if field_is_type('list:string'):
|
||||
obj = map(str,obj)
|
||||
else:
|
||||
obj = map(int,obj)
|
||||
return 'ARRAY[%s]' % ','.join(repr(item) for item in obj)
|
||||
return BaseAdapter.represent(self, obj, fieldtype)
|
||||
|
||||
|
||||
class JDBCPostgreSQLAdapter(PostgreSQLAdapter):
|
||||
drivers = ('zxJDBC',)
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:/]+)(\:(?P<port>[0-9]+))?/(?P<db>.+)$')
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None ):
|
||||
self.db = db
|
||||
self.dbengine = "postgres"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError("Invalid URI string in DAL")
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
port = m.group('port') or '5432'
|
||||
msg = ('jdbc:postgresql://%s:%s/%s' % (host, port, db), user, password)
|
||||
def connector(msg=msg,driver_args=driver_args):
|
||||
return self.driver.connect(*msg,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.connection.set_client_encoding('UTF8')
|
||||
self.execute('BEGIN;')
|
||||
self.execute("SET CLIENT_ENCODING TO 'UNICODE';")
|
||||
self.try_json()
|
||||
@@ -1,97 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class SAPDBAdapter(BaseAdapter):
|
||||
drivers = ('sapdb',)
|
||||
|
||||
support_distributed_transaction = False
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'LONG',
|
||||
'json': 'LONG',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'LONG',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'FLOAT',
|
||||
'double': 'DOUBLE PRECISION',
|
||||
'decimal': 'FIXED(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'TIMESTAMP',
|
||||
'id': 'INT PRIMARY KEY',
|
||||
'reference': 'INT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
'list:integer': 'LONG',
|
||||
'list:string': 'LONG',
|
||||
'list:reference': 'LONG',
|
||||
'big-id': 'BIGINT PRIMARY KEY',
|
||||
'big-reference': 'BIGINT, FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||
}
|
||||
|
||||
def sequence_name(self,table):
|
||||
return (self.QUOTE_TEMPLATE + '_id_Seq') % table
|
||||
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
if len(sql_w) > 1:
|
||||
sql_w_row = sql_w + ' AND w_row > %i' % lmin
|
||||
else:
|
||||
sql_w_row = 'WHERE w_row > %i' % lmin
|
||||
return '%s %s FROM (SELECT w_tmp.*, ROWNO w_row FROM (SELECT %s FROM %s%s%s) w_tmp WHERE ROWNO=%i) %s %s %s;' % (sql_s, sql_f, sql_f, sql_t, sql_w, sql_o, lmax, sql_t, sql_w_row, sql_o)
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def create_sequence_and_triggers(self, query, table, **args):
|
||||
# following lines should only be executed if table._sequence_name does not exist
|
||||
self.execute('CREATE SEQUENCE %s;' % table._sequence_name)
|
||||
self.execute("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT NEXTVAL('%s');" \
|
||||
% (table._tablename, table._id.name, table._sequence_name))
|
||||
self.execute(query)
|
||||
|
||||
REGEX_URI = re.compile('^(?P<user>[^:@]+)(\:(?P<password>[^@]*))?@(?P<host>[^\:@]+)(\:(?P<port>[0-9]+))?/(?P<db>[^\?]+)(\?sslmode=(?P<sslmode>.+))?$')
|
||||
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "sapdb"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://',1)[1]
|
||||
m = self.REGEX_URI.match(ruri)
|
||||
if not m:
|
||||
raise SyntaxError("Invalid URI string in DAL")
|
||||
user = credential_decoder(m.group('user'))
|
||||
if not user:
|
||||
raise SyntaxError('User required')
|
||||
password = credential_decoder(m.group('password'))
|
||||
if not password:
|
||||
password = ''
|
||||
host = m.group('host')
|
||||
if not host:
|
||||
raise SyntaxError('Host name required')
|
||||
db = m.group('db')
|
||||
if not db:
|
||||
raise SyntaxError('Database name required')
|
||||
def connector(user=user, password=password, database=db,
|
||||
host=host, driver_args=driver_args):
|
||||
return self.driver.Connection(user, password, database,
|
||||
host, **driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def lastrowid(self,table):
|
||||
self.execute("select %s.NEXTVAL from dual" % table._sequence_name)
|
||||
return long(self.cursor.fetchone()[0])
|
||||
@@ -1,280 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import copy
|
||||
import datetime
|
||||
import locale
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
from .._compat import PY2, pjoin
|
||||
from .._globals import IDENTITY
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class SQLiteAdapter(BaseAdapter):
|
||||
drivers = ('sqlite2','sqlite3')
|
||||
|
||||
can_select_for_update = None # support ourselves with BEGIN TRANSACTION
|
||||
|
||||
def EXTRACT(self,field,what):
|
||||
return "web2py_extract('%s',%s)" % (what, self.expand(field))
|
||||
|
||||
@staticmethod
|
||||
def web2py_extract(lookup, s):
|
||||
table = {
|
||||
'year': (0, 4),
|
||||
'month': (5, 7),
|
||||
'day': (8, 10),
|
||||
'hour': (11, 13),
|
||||
'minute': (14, 16),
|
||||
'second': (17, 19),
|
||||
}
|
||||
try:
|
||||
if lookup != 'epoch':
|
||||
(i, j) = table[lookup]
|
||||
return int(s[i:j])
|
||||
else:
|
||||
return time.mktime(datetime.datetime.strptime(s, '%Y-%m-%d %H:%M:%S').timetuple())
|
||||
except:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def web2py_regexp(expression, item):
|
||||
return re.compile(expression).search(item) is not None
|
||||
|
||||
def __init__(self, db, uri, pool_size=0, folder=None, db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "sqlite"
|
||||
self.uri = uri
|
||||
self.adapter_args = adapter_args
|
||||
if do_connect: self.find_driver(adapter_args)
|
||||
self.pool_size = 0
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
path_encoding = sys.getfilesystemencoding() \
|
||||
or locale.getdefaultlocale()[1] or 'utf8'
|
||||
if uri.startswith('sqlite:memory'):
|
||||
self.dbpath = ':memory:'
|
||||
else:
|
||||
self.dbpath = uri.split('://',1)[1]
|
||||
if self.dbpath[0] != '/':
|
||||
if PY2:
|
||||
self.dbpath = pjoin(
|
||||
self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
|
||||
else:
|
||||
self.dbpath = pjoin(self.folder, self.dbpath)
|
||||
if not 'check_same_thread' in driver_args:
|
||||
driver_args['check_same_thread'] = False
|
||||
if not 'detect_types' in driver_args and do_connect:
|
||||
driver_args['detect_types'] = self.driver.PARSE_DECLTYPES
|
||||
def connector(dbpath=self.dbpath, driver_args=driver_args):
|
||||
return self.driver.Connection(dbpath, **driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.connection.create_function('web2py_extract', 2,
|
||||
SQLiteAdapter.web2py_extract)
|
||||
self.connection.create_function("REGEXP", 2,
|
||||
SQLiteAdapter.web2py_regexp)
|
||||
|
||||
if self.adapter_args.get('foreign_keys',True):
|
||||
self.execute('PRAGMA foreign_keys=ON;')
|
||||
|
||||
def _truncate(self, table, mode=''):
|
||||
tablename = table._tablename
|
||||
return ['DELETE FROM %s;' % tablename,
|
||||
"DELETE FROM sqlite_sequence WHERE name='%s';" % tablename]
|
||||
|
||||
def lastrowid(self, table):
|
||||
return self.cursor.lastrowid
|
||||
|
||||
def REGEXP(self,first,second):
|
||||
return '(%s REGEXP %s)' % (self.expand(first),
|
||||
self.expand(second,'string'))
|
||||
|
||||
def delete(self, tablename, query):
|
||||
# SQLite requires its own delete to handle CASCADE
|
||||
db = self.db
|
||||
table = db[tablename]
|
||||
deleted = [x[table._id.name] for x in db(query).select(table._id)]
|
||||
|
||||
counter = super(SQLiteAdapter, self).delete(tablename, query)
|
||||
|
||||
if counter:
|
||||
for field in table._referenced_by:
|
||||
if field.type == 'reference '+ tablename \
|
||||
and field.ondelete == 'CASCADE':
|
||||
db(field.belongs(deleted)).delete()
|
||||
|
||||
return counter
|
||||
|
||||
def select(self, query, fields, attributes):
|
||||
"""
|
||||
Simulate `SELECT ... FOR UPDATE` with `BEGIN IMMEDIATE TRANSACTION`.
|
||||
Note that the entire database, rather than one record, is locked
|
||||
(it will be locked eventually anyway by the following UPDATE).
|
||||
"""
|
||||
if attributes.get('for_update', False) and not 'cache' in attributes:
|
||||
self.execute('BEGIN IMMEDIATE TRANSACTION;')
|
||||
return super(SQLiteAdapter, self).select(query, fields, attributes)
|
||||
|
||||
|
||||
SPATIALLIBS = {
|
||||
'Windows':'libspatialite',
|
||||
'Linux':'libspatialite.so',
|
||||
'Darwin':'libspatialite.dylib'
|
||||
}
|
||||
|
||||
class SpatiaLiteAdapter(SQLiteAdapter):
|
||||
drivers = ('sqlite3','sqlite2')
|
||||
|
||||
types = copy.copy(BaseAdapter.types)
|
||||
types.update(geometry='GEOMETRY')
|
||||
|
||||
def __init__(self, db, uri, pool_size=0, folder=None, db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, srid=4326, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "spatialite"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args)
|
||||
self.pool_size = 0
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
self.srid = srid
|
||||
path_encoding = sys.getfilesystemencoding() \
|
||||
or locale.getdefaultlocale()[1] or 'utf8'
|
||||
if uri.startswith('spatialite:memory'):
|
||||
self.dbpath = ':memory:'
|
||||
else:
|
||||
self.dbpath = uri.split('://',1)[1]
|
||||
if self.dbpath[0] != '/':
|
||||
self.dbpath = pjoin(
|
||||
self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
|
||||
if not 'check_same_thread' in driver_args:
|
||||
driver_args['check_same_thread'] = False
|
||||
if not 'detect_types' in driver_args and do_connect:
|
||||
driver_args['detect_types'] = self.driver.PARSE_DECLTYPES
|
||||
def connector(dbpath=self.dbpath, driver_args=driver_args):
|
||||
return self.driver.Connection(dbpath, **driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
self.connection.enable_load_extension(True)
|
||||
# for Windows, rename libspatialite-2.dll to libspatialite.dll
|
||||
# Linux uses libspatialite.so
|
||||
# Mac OS X uses libspatialite.dylib
|
||||
libspatialite = SPATIALLIBS[platform.system()]
|
||||
self.execute(r'SELECT load_extension("%s");' % libspatialite)
|
||||
|
||||
self.connection.create_function('web2py_extract', 2,
|
||||
SQLiteAdapter.web2py_extract)
|
||||
self.connection.create_function("REGEXP", 2,
|
||||
SQLiteAdapter.web2py_regexp)
|
||||
|
||||
# GIS functions
|
||||
|
||||
def ST_ASGEOJSON(self, first, second):
|
||||
return 'AsGeoJSON(%s,%s,%s)' %(self.expand(first),
|
||||
second['precision'], second['options'])
|
||||
|
||||
def ST_ASTEXT(self, first):
|
||||
return 'AsText(%s)' %(self.expand(first))
|
||||
|
||||
def ST_CONTAINS(self, first, second):
|
||||
return 'Contains(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def ST_DISTANCE(self, first, second):
|
||||
return 'Distance(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def ST_EQUALS(self, first, second):
|
||||
return 'Equals(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def ST_INTERSECTS(self, first, second):
|
||||
return 'Intersects(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def ST_OVERLAPS(self, first, second):
|
||||
return 'Overlaps(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def ST_SIMPLIFY(self, first, second):
|
||||
return 'Simplify(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, 'double'))
|
||||
|
||||
def ST_TOUCHES(self, first, second):
|
||||
return 'Touches(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def ST_WITHIN(self, first, second):
|
||||
return 'Within(%s,%s)' %(self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
|
||||
def represent(self, obj, fieldtype):
|
||||
field_is_type = fieldtype.startswith
|
||||
if field_is_type('geo'):
|
||||
srid = 4326 # Spatialite default srid for geometry
|
||||
geotype, parms = fieldtype[:-1].split('(')
|
||||
parms = parms.split(',')
|
||||
if len(parms) >= 2:
|
||||
schema, srid = parms[:2]
|
||||
# if field_is_type('geometry'):
|
||||
value = "ST_GeomFromText('%s',%s)" %(obj, srid)
|
||||
# elif field_is_type('geography'):
|
||||
# value = "ST_GeogFromText('SRID=%s;%s')" %(srid, obj)
|
||||
# else:
|
||||
# raise SyntaxError, 'Invalid field type %s' %fieldtype
|
||||
return value
|
||||
return BaseAdapter.represent(self, obj, fieldtype)
|
||||
|
||||
|
||||
class JDBCSQLiteAdapter(SQLiteAdapter):
|
||||
drivers = ('zxJDBC_sqlite',)
|
||||
|
||||
def __init__(self, db, uri, pool_size=0, folder=None, db_codec='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "sqlite"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
path_encoding = sys.getfilesystemencoding() \
|
||||
or locale.getdefaultlocale()[1] or 'utf8'
|
||||
if uri.startswith('sqlite:memory'):
|
||||
self.dbpath = ':memory:'
|
||||
else:
|
||||
self.dbpath = uri.split('://',1)[1]
|
||||
if self.dbpath[0] != '/':
|
||||
self.dbpath = pjoin(
|
||||
self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
|
||||
def connector(dbpath=self.dbpath,driver_args=driver_args):
|
||||
return self.driver.connect(
|
||||
self.driver.getConnection('jdbc:sqlite:'+dbpath),
|
||||
**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def after_connection(self):
|
||||
# FIXME http://www.zentus.com/sqlitejdbc/custom_functions.html for UDFs
|
||||
self.connection.create_function('web2py_extract', 2,
|
||||
SQLiteAdapter.web2py_extract)
|
||||
|
||||
def execute(self, a):
|
||||
return self.log_execute(a)
|
||||
@@ -1,76 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .._globals import IDENTITY
|
||||
from ..connection import ConnectionPool
|
||||
from .base import BaseAdapter
|
||||
|
||||
|
||||
class TeradataAdapter(BaseAdapter):
|
||||
drivers = ('pyodbc',)
|
||||
|
||||
types = {
|
||||
'boolean': 'CHAR(1)',
|
||||
'string': 'VARCHAR(%(length)s)',
|
||||
'text': 'VARCHAR(2000)',
|
||||
'json': 'VARCHAR(4000)',
|
||||
'password': 'VARCHAR(%(length)s)',
|
||||
'blob': 'BLOB',
|
||||
'upload': 'VARCHAR(%(length)s)',
|
||||
'integer': 'INT',
|
||||
'bigint': 'BIGINT',
|
||||
'float': 'REAL',
|
||||
'double': 'DOUBLE',
|
||||
'decimal': 'NUMERIC(%(precision)s,%(scale)s)',
|
||||
'date': 'DATE',
|
||||
'time': 'TIME',
|
||||
'datetime': 'TIMESTAMP',
|
||||
# Modified Constraint syntax for Teradata.
|
||||
# Teradata does not support ON DELETE.
|
||||
'id': 'INT GENERATED ALWAYS AS IDENTITY', # Teradata Specific
|
||||
'reference': 'INT',
|
||||
'list:integer': 'VARCHAR(4000)',
|
||||
'list:string': 'VARCHAR(4000)',
|
||||
'list:reference': 'VARCHAR(4000)',
|
||||
'big-id': 'BIGINT GENERATED ALWAYS AS IDENTITY', # Teradata Specific
|
||||
'big-reference': 'BIGINT',
|
||||
'reference FK': ' REFERENCES %(foreign_key)s',
|
||||
'reference TFK': ' FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s)',
|
||||
}
|
||||
|
||||
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}, do_connect=True, after_connection=None):
|
||||
self.db = db
|
||||
self.dbengine = "teradata"
|
||||
self.uri = uri
|
||||
if do_connect: self.find_driver(adapter_args,uri)
|
||||
self.pool_size = pool_size
|
||||
self.folder = folder
|
||||
self.db_codec = db_codec
|
||||
self._after_connection = after_connection
|
||||
self.find_or_make_work_folder()
|
||||
ruri = uri.split('://', 1)[1]
|
||||
def connector(cnxn=ruri,driver_args=driver_args):
|
||||
return self.driver.connect(cnxn,**driver_args)
|
||||
self.connector = connector
|
||||
if do_connect: self.reconnect()
|
||||
|
||||
def close(self,action='commit',really=True):
|
||||
# Teradata does not implicitly close off the cursor
|
||||
# leading to SQL_ACTIVE_STATEMENTS limit errors
|
||||
self.cursor.close()
|
||||
ConnectionPool.close(self, action, really)
|
||||
|
||||
def LEFT_JOIN(self):
|
||||
return 'LEFT OUTER JOIN'
|
||||
|
||||
# Similar to MSSQL, Teradata can't specify a range (for Pageby)
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
sql_s += ' TOP %i' % lmax
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
def _truncate(self, table, mode=''):
|
||||
tablename = table._tablename
|
||||
return ['DELETE FROM %s ALL;' % (tablename)]
|
||||
-1095
File diff suppressed because it is too large
Load Diff
@@ -1,116 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
from ._compat import exists
|
||||
from ._globals import GLOBAL_LOCKER, THREAD_LOCAL
|
||||
from .helpers.classes import UseDatabaseStoredFile
|
||||
|
||||
class ConnectionPool(object):
|
||||
|
||||
POOLS = {}
|
||||
check_active_connection = True
|
||||
|
||||
@staticmethod
|
||||
def set_folder(folder):
|
||||
THREAD_LOCAL.folder = folder
|
||||
|
||||
# ## this allows gluon to commit/rollback all dbs in this thread
|
||||
|
||||
def close(self,action='commit',really=True):
|
||||
if action:
|
||||
if callable(action):
|
||||
action(self)
|
||||
else:
|
||||
getattr(self, action)()
|
||||
# ## if you want pools, recycle this connection
|
||||
if self.pool_size:
|
||||
GLOBAL_LOCKER.acquire()
|
||||
pool = ConnectionPool.POOLS[self.uri]
|
||||
if len(pool) < self.pool_size:
|
||||
pool.append(self.connection)
|
||||
really = False
|
||||
GLOBAL_LOCKER.release()
|
||||
if really:
|
||||
self.close_connection()
|
||||
self.connection = None
|
||||
|
||||
@staticmethod
|
||||
def close_all_instances(action):
|
||||
""" to close cleanly databases in a multithreaded environment """
|
||||
dbs = getattr(THREAD_LOCAL,'db_instances',{}).items()
|
||||
for db_uid, db_group in dbs:
|
||||
for db in db_group:
|
||||
if hasattr(db,'_adapter'):
|
||||
db._adapter.close(action)
|
||||
getattr(THREAD_LOCAL,'db_instances',{}).clear()
|
||||
getattr(THREAD_LOCAL,'db_instances_zombie',{}).clear()
|
||||
if callable(action):
|
||||
action(None)
|
||||
return
|
||||
|
||||
def find_or_make_work_folder(self):
|
||||
#this actually does not make the folder. it has to be there
|
||||
self.folder = getattr(THREAD_LOCAL,'folder','')
|
||||
|
||||
if (os.path.isabs(self.folder) and
|
||||
isinstance(self, UseDatabaseStoredFile) and
|
||||
self.folder.startswith(os.getcwd())):
|
||||
self.folder = os.path.relpath(self.folder, os.getcwd())
|
||||
|
||||
# Creating the folder if it does not exist
|
||||
if False and self.folder and not exists(self.folder):
|
||||
os.mkdir(self.folder)
|
||||
|
||||
def after_connection_hook(self):
|
||||
"""Hook for the after_connection parameter"""
|
||||
if callable(self._after_connection):
|
||||
self._after_connection(self)
|
||||
self.after_connection()
|
||||
|
||||
def after_connection(self):
|
||||
#this it is supposed to be overloaded by adapters
|
||||
pass
|
||||
|
||||
def reconnect(self, f=None, cursor=True):
|
||||
"""
|
||||
Defines: `self.connection` and `self.cursor`
|
||||
(if cursor is True)
|
||||
if `self.pool_size>0` it will try pull the connection from the pool
|
||||
if the connection is not active (closed by db server) it will loop
|
||||
if not `self.pool_size` or no active connections in pool makes a new one
|
||||
"""
|
||||
if getattr(self,'connection', None) is not None:
|
||||
return
|
||||
if f is None:
|
||||
f = self.connector
|
||||
|
||||
# if not hasattr(self, "driver") or self.driver is None:
|
||||
# LOGGER.debug("Skipping connection since there's no driver")
|
||||
# return
|
||||
|
||||
if not self.pool_size:
|
||||
self.connection = f()
|
||||
self.cursor = cursor and self.connection.cursor()
|
||||
else:
|
||||
uri = self.uri
|
||||
POOLS = ConnectionPool.POOLS
|
||||
while True:
|
||||
GLOBAL_LOCKER.acquire()
|
||||
if not uri in POOLS:
|
||||
POOLS[uri] = []
|
||||
if POOLS[uri]:
|
||||
self.connection = POOLS[uri].pop()
|
||||
GLOBAL_LOCKER.release()
|
||||
self.cursor = cursor and self.connection.cursor()
|
||||
try:
|
||||
if self.cursor and self.check_active_connection:
|
||||
self.execute('SELECT 1;')
|
||||
break
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
GLOBAL_LOCKER.release()
|
||||
self.connection = f()
|
||||
self.cursor = cursor and self.connection.cursor()
|
||||
break
|
||||
self.after_connection_hook()
|
||||
@@ -1,298 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import copy
|
||||
import marshal
|
||||
import struct
|
||||
import traceback
|
||||
|
||||
from .._compat import exists, copyreg
|
||||
from .._globals import LOGGER
|
||||
|
||||
|
||||
class Reference(long):
|
||||
|
||||
def __allocate(self):
|
||||
if not self._record:
|
||||
self._record = self._table[long(self)]
|
||||
if not self._record:
|
||||
raise RuntimeError(
|
||||
"Using a recursive select but encountered a broken reference: %s %d"%(self._table, long(self)))
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key == 'id':
|
||||
return long(self)
|
||||
if key in self._table:
|
||||
self.__allocate()
|
||||
if self._record:
|
||||
return self._record.get(key,None) # to deal with case self.update_record()
|
||||
else:
|
||||
return None
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.__getattr__(key, default)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key.startswith('_'):
|
||||
long.__setattr__(self, key, value)
|
||||
return
|
||||
self.__allocate()
|
||||
self._record[key] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == 'id':
|
||||
return long(self)
|
||||
self.__allocate()
|
||||
return self._record.get(key, None)
|
||||
|
||||
def __setitem__(self,key,value):
|
||||
self.__allocate()
|
||||
self._record[key] = value
|
||||
|
||||
def Reference_unpickler(data):
|
||||
return marshal.loads(data)
|
||||
|
||||
def Reference_pickler(data):
|
||||
try:
|
||||
marshal_dump = marshal.dumps(long(data))
|
||||
except AttributeError:
|
||||
marshal_dump = 'i%s' % struct.pack('<i', long(data))
|
||||
return (Reference_unpickler, (marshal_dump,))
|
||||
|
||||
copyreg.pickle(Reference, Reference_pickler, Reference_unpickler)
|
||||
|
||||
|
||||
class SQLCallableList(list):
|
||||
def __call__(self):
|
||||
return copy.copy(self)
|
||||
|
||||
|
||||
class SQLALL(object):
|
||||
"""
|
||||
Helper class providing a comma-separated string having all the field names
|
||||
(prefixed by table name and '.')
|
||||
|
||||
normally only called from within gluon.dal
|
||||
"""
|
||||
|
||||
def __init__(self, table):
|
||||
self._table = table
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join([str(field) for field in self._table])
|
||||
|
||||
|
||||
class SQLCustomType(object):
|
||||
"""
|
||||
Allows defining of custom SQL types
|
||||
|
||||
Args:
|
||||
type: the web2py type (default = 'string')
|
||||
native: the backend type
|
||||
encoder: how to encode the value to store it in the backend
|
||||
decoder: how to decode the value retrieved from the backend
|
||||
validator: what validators to use ( default = None, will use the
|
||||
default validator for type)
|
||||
|
||||
Example::
|
||||
Define as:
|
||||
|
||||
decimal = SQLCustomType(
|
||||
type ='double',
|
||||
native ='integer',
|
||||
encoder =(lambda x: int(float(x) * 100)),
|
||||
decoder = (lambda x: Decimal("0.00") + Decimal(str(float(x)/100)) )
|
||||
)
|
||||
|
||||
db.define_table(
|
||||
'example',
|
||||
Field('value', type=decimal)
|
||||
)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type='string',
|
||||
native=None,
|
||||
encoder=None,
|
||||
decoder=None,
|
||||
validator=None,
|
||||
_class=None,
|
||||
):
|
||||
|
||||
self.type = type
|
||||
self.native = native
|
||||
self.encoder = encoder or (lambda x: x)
|
||||
self.decoder = decoder or (lambda x: x)
|
||||
self.validator = validator
|
||||
self._class = _class or type
|
||||
|
||||
def startswith(self, text=None):
|
||||
try:
|
||||
return self.type.startswith(self, text)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
def endswith(self, text=None):
|
||||
try:
|
||||
return self.type.endswith(self, text)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
def __getslice__(self, a=0, b=100):
|
||||
return None
|
||||
|
||||
def __getitem__(self, i):
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return self._class
|
||||
|
||||
|
||||
class RecordUpdater(object):
|
||||
def __init__(self, colset, table, id):
|
||||
self.colset, self.db, self.tablename, self.id = \
|
||||
colset, table._db, table._tablename, id
|
||||
|
||||
def __call__(self, **fields):
|
||||
colset, db, tablename, id = self.colset, self.db, self.tablename, self.id
|
||||
table = db[tablename]
|
||||
newfields = fields or dict(colset)
|
||||
for fieldname in newfields.keys():
|
||||
if not fieldname in table.fields or table[fieldname].type=='id':
|
||||
del newfields[fieldname]
|
||||
table._db(table._id==id,ignore_common_filters=True).update(**newfields)
|
||||
colset.update(newfields)
|
||||
return colset
|
||||
|
||||
class RecordDeleter(object):
|
||||
def __init__(self, table, id):
|
||||
self.db, self.tablename, self.id = table._db, table._tablename, id
|
||||
def __call__(self):
|
||||
return self.db(self.db[self.tablename]._id==self.id).delete()
|
||||
|
||||
|
||||
class MethodAdder(object):
|
||||
def __init__(self,table):
|
||||
self.table = table
|
||||
def __call__(self):
|
||||
return self.register()
|
||||
def __getattr__(self,method_name):
|
||||
return self.register(method_name)
|
||||
def register(self,method_name=None):
|
||||
def _decorated(f):
|
||||
instance = self.table
|
||||
import types
|
||||
method = types.MethodType(f, instance, instance.__class__)
|
||||
name = method_name or f.func_name
|
||||
setattr(instance, name, method)
|
||||
return f
|
||||
return _decorated
|
||||
|
||||
|
||||
class DatabaseStoredFile:
|
||||
|
||||
web2py_filesystems = set()
|
||||
|
||||
def escape(self,obj):
|
||||
return self.db._adapter.escape(obj)
|
||||
|
||||
@staticmethod
|
||||
def try_create_web2py_filesystem(db):
|
||||
if not db._uri in DatabaseStoredFile.web2py_filesystems:
|
||||
if db._adapter.dbengine == 'mysql':
|
||||
sql = "CREATE TABLE IF NOT EXISTS web2py_filesystem (path VARCHAR(255), content LONGTEXT, PRIMARY KEY(path) ) ENGINE=InnoDB;"
|
||||
elif db._adapter.dbengine in ('postgres', 'sqlite'):
|
||||
sql = "CREATE TABLE IF NOT EXISTS web2py_filesystem (path VARCHAR(255), content TEXT, PRIMARY KEY(path));"
|
||||
db.executesql(sql)
|
||||
DatabaseStoredFile.web2py_filesystems.add(db._uri)
|
||||
|
||||
def __init__(self,db,filename,mode):
|
||||
if not db._adapter.dbengine in ('mysql', 'postgres', 'sqlite'):
|
||||
raise RuntimeError("only MySQL/Postgres/SQLite can store metadata .table files in database for now")
|
||||
self.db = db
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
DatabaseStoredFile.try_create_web2py_filesystem(db)
|
||||
self.p=0
|
||||
self.data = ''
|
||||
if mode in ('r','rw','a'):
|
||||
query = "SELECT content FROM web2py_filesystem WHERE path='%s'" \
|
||||
% filename
|
||||
rows = self.db.executesql(query)
|
||||
if rows:
|
||||
self.data = rows[0][0]
|
||||
elif exists(filename):
|
||||
datafile = open(filename, 'r')
|
||||
try:
|
||||
self.data = datafile.read()
|
||||
finally:
|
||||
datafile.close()
|
||||
elif mode in ('r','rw'):
|
||||
raise RuntimeError("File %s does not exist" % filename)
|
||||
|
||||
def read(self, bytes):
|
||||
data = self.data[self.p:self.p+bytes]
|
||||
self.p += len(data)
|
||||
return data
|
||||
|
||||
def readline(self):
|
||||
i = self.data.find('\n',self.p)+1
|
||||
if i>0:
|
||||
data, self.p = self.data[self.p:i], i
|
||||
else:
|
||||
data, self.p = self.data[self.p:], len(self.data)
|
||||
return data
|
||||
|
||||
def write(self,data):
|
||||
self.data += data
|
||||
|
||||
def close_connection(self):
|
||||
if self.db is not None:
|
||||
self.db.executesql(
|
||||
"DELETE FROM web2py_filesystem WHERE path='%s'" % self.filename)
|
||||
query = "INSERT INTO web2py_filesystem(path,content) VALUES ('%s','%s')"\
|
||||
% (self.filename, self.data.replace("'","''"))
|
||||
self.db.executesql(query)
|
||||
self.db.commit()
|
||||
self.db = None
|
||||
|
||||
def close(self):
|
||||
self.close_connection()
|
||||
|
||||
@staticmethod
|
||||
def exists(db, filename):
|
||||
if exists(filename):
|
||||
return True
|
||||
|
||||
DatabaseStoredFile.try_create_web2py_filesystem(db)
|
||||
|
||||
query = "SELECT path FROM web2py_filesystem WHERE path='%s'" % filename
|
||||
try:
|
||||
if db.executesql(query):
|
||||
return True
|
||||
except Exception, e:
|
||||
if not (db._adapter.isOperationalError(e) or
|
||||
db._adapter.isProgrammingError(e)):
|
||||
raise
|
||||
# no web2py_filesystem found?
|
||||
tb = traceback.format_exc()
|
||||
LOGGER.error("Could not retrieve %s\n%s" % (filename, tb))
|
||||
return False
|
||||
|
||||
|
||||
class UseDatabaseStoredFile:
|
||||
|
||||
def file_exists(self, filename):
|
||||
return DatabaseStoredFile.exists(self.db,filename)
|
||||
|
||||
def file_open(self, filename, mode='rb', lock=True):
|
||||
return DatabaseStoredFile(self.db,filename,mode)
|
||||
|
||||
def file_close(self, fileobj):
|
||||
fileobj.close_connection()
|
||||
|
||||
def file_delete(self,filename):
|
||||
query = "DELETE FROM web2py_filesystem WHERE path='%s'" % filename
|
||||
self.db.executesql(query)
|
||||
self.db.commit()
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import uuid
|
||||
import re
|
||||
|
||||
from .regex import REGEX_NOPASSWD, REGEX_UNPACK, REGEX_CONST_STRING, REGEX_W
|
||||
from .classes import SQLCustomType
|
||||
#from ..objects import Field, Table
|
||||
|
||||
|
||||
PLURALIZE_RULES = [
|
||||
(re.compile('child$'), re.compile('child$'), 'children'),
|
||||
(re.compile('oot$'), re.compile('oot$'), 'eet'),
|
||||
(re.compile('ooth$'), re.compile('ooth$'), 'eeth'),
|
||||
(re.compile('l[eo]af$'), re.compile('l([eo])af$'), 'l\\1aves'),
|
||||
(re.compile('sis$'), re.compile('sis$'), 'ses'),
|
||||
(re.compile('man$'), re.compile('man$'), 'men'),
|
||||
(re.compile('ife$'), re.compile('ife$'), 'ives'),
|
||||
(re.compile('eau$'), re.compile('eau$'), 'eaux'),
|
||||
(re.compile('lf$'), re.compile('lf$'), 'lves'),
|
||||
(re.compile('[sxz]$'), re.compile('$'), 'es'),
|
||||
(re.compile('[^aeioudgkprt]h$'), re.compile('$'), 'es'),
|
||||
(re.compile('(qu|[^aeiou])y$'), re.compile('y$'), 'ies'),
|
||||
(re.compile('$'), re.compile('$'), 's'),
|
||||
]
|
||||
|
||||
def pluralize(singular, rules=PLURALIZE_RULES):
|
||||
for line in rules:
|
||||
re_search, re_sub, replace = line
|
||||
plural = re_search.search(singular) and re_sub.sub(replace, singular)
|
||||
if plural: return plural
|
||||
|
||||
def hide_password(uri):
|
||||
if isinstance(uri,(list,tuple)):
|
||||
return [hide_password(item) for item in uri]
|
||||
return REGEX_NOPASSWD.sub('******',uri)
|
||||
|
||||
|
||||
def cleanup(text):
|
||||
"""
|
||||
Validates that the given text is clean: only contains [0-9a-zA-Z_]
|
||||
"""
|
||||
#if not REGEX_ALPHANUMERIC.match(text):
|
||||
# raise SyntaxError('invalid table or field name: %s' % text)
|
||||
return text
|
||||
|
||||
|
||||
def list_represent(x,r=None):
|
||||
return ', '.join(str(y) for y in x or [])
|
||||
|
||||
|
||||
def xorify(orderby):
|
||||
if not orderby:
|
||||
return None
|
||||
orderby2 = orderby[0]
|
||||
for item in orderby[1:]:
|
||||
orderby2 = orderby2 | item
|
||||
return orderby2
|
||||
|
||||
|
||||
def use_common_filters(query):
|
||||
return (query and hasattr(query,'ignore_common_filters') and \
|
||||
not query.ignore_common_filters)
|
||||
|
||||
|
||||
def bar_escape(item):
|
||||
return str(item).replace('|', '||')
|
||||
|
||||
|
||||
def bar_encode(items):
|
||||
return '|%s|' % '|'.join(bar_escape(item) for item in items if str(item).strip())
|
||||
|
||||
|
||||
def bar_decode_integer(value):
|
||||
if not hasattr(value,'split') and hasattr(value,'read'):
|
||||
value = value.read()
|
||||
return [long(x) for x in value.split('|') if x.strip()]
|
||||
|
||||
|
||||
def bar_decode_string(value):
|
||||
return [x.replace('||', '|') for x in
|
||||
REGEX_UNPACK.split(value[1:-1]) if x.strip()]
|
||||
|
||||
|
||||
def archive_record(qset, fs, archive_table, current_record):
|
||||
tablenames = qset.db._adapter.tables(qset.query)
|
||||
if len(tablenames) != 1:
|
||||
raise RuntimeError("cannot update join")
|
||||
for row in qset.select():
|
||||
fields = archive_table._filter_fields(row)
|
||||
fields[current_record] = row.id
|
||||
archive_table.insert(**fields)
|
||||
return False
|
||||
|
||||
|
||||
def smart_query(fields,text):
|
||||
from ..objects import Field, Table
|
||||
if not isinstance(fields,(list,tuple)):
|
||||
fields = [fields]
|
||||
new_fields = []
|
||||
for field in fields:
|
||||
if isinstance(field,Field):
|
||||
new_fields.append(field)
|
||||
elif isinstance(field,Table):
|
||||
for ofield in field:
|
||||
new_fields.append(ofield)
|
||||
else:
|
||||
raise RuntimeError("fields must be a list of fields")
|
||||
fields = new_fields
|
||||
field_map = {}
|
||||
for field in fields:
|
||||
n = field.name.lower()
|
||||
if not n in field_map:
|
||||
field_map[n] = field
|
||||
n = str(field).lower()
|
||||
if not n in field_map:
|
||||
field_map[n] = field
|
||||
constants = {}
|
||||
i = 0
|
||||
while True:
|
||||
m = REGEX_CONST_STRING.search(text)
|
||||
if not m: break
|
||||
text = text[:m.start()]+('#%i' % i)+text[m.end():]
|
||||
constants[str(i)] = m.group()[1:-1]
|
||||
i+=1
|
||||
text = re.sub('\s+',' ',text).lower()
|
||||
for a,b in [('&','and'),
|
||||
('|','or'),
|
||||
('~','not'),
|
||||
('==','='),
|
||||
('<','<'),
|
||||
('>','>'),
|
||||
('<=','<='),
|
||||
('>=','>='),
|
||||
('<>','!='),
|
||||
('=<','<='),
|
||||
('=>','>='),
|
||||
('=','='),
|
||||
(' less or equal than ','<='),
|
||||
(' greater or equal than ','>='),
|
||||
(' equal or less than ','<='),
|
||||
(' equal or greater than ','>='),
|
||||
(' less or equal ','<='),
|
||||
(' greater or equal ','>='),
|
||||
(' equal or less ','<='),
|
||||
(' equal or greater ','>='),
|
||||
(' not equal to ','!='),
|
||||
(' not equal ','!='),
|
||||
(' equal to ','='),
|
||||
(' equal ','='),
|
||||
(' equals ','='),
|
||||
(' less than ','<'),
|
||||
(' greater than ','>'),
|
||||
(' starts with ','startswith'),
|
||||
(' ends with ','endswith'),
|
||||
(' not in ' , 'notbelongs'),
|
||||
(' in ' , 'belongs'),
|
||||
(' is ','=')]:
|
||||
if a[0]==' ':
|
||||
text = text.replace(' is'+a,' %s ' % b)
|
||||
text = text.replace(a,' %s ' % b)
|
||||
text = re.sub('\s+',' ',text).lower()
|
||||
text = re.sub('(?P<a>[\<\>\!\=])\s+(?P<b>[\<\>\!\=])','\g<a>\g<b>',text)
|
||||
query = field = neg = op = logic = None
|
||||
for item in text.split():
|
||||
if field is None:
|
||||
if item == 'not':
|
||||
neg = True
|
||||
elif not neg and not logic and item in ('and','or'):
|
||||
logic = item
|
||||
elif item in field_map:
|
||||
field = field_map[item]
|
||||
else:
|
||||
raise RuntimeError("Invalid syntax")
|
||||
elif not field is None and op is None:
|
||||
op = item
|
||||
elif not op is None:
|
||||
if item.startswith('#'):
|
||||
if not item[1:] in constants:
|
||||
raise RuntimeError("Invalid syntax")
|
||||
value = constants[item[1:]]
|
||||
else:
|
||||
value = item
|
||||
if field.type in ('text', 'string', 'json'):
|
||||
if op == '=': op = 'like'
|
||||
if op == '=': new_query = field==value
|
||||
elif op == '<': new_query = field<value
|
||||
elif op == '>': new_query = field>value
|
||||
elif op == '<=': new_query = field<=value
|
||||
elif op == '>=': new_query = field>=value
|
||||
elif op == '!=': new_query = field!=value
|
||||
elif op == 'belongs': new_query = field.belongs(value.split(','))
|
||||
elif op == 'notbelongs': new_query = ~field.belongs(value.split(','))
|
||||
elif field.type in ('text', 'string', 'json'):
|
||||
if op == 'contains': new_query = field.contains(value)
|
||||
elif op == 'like': new_query = field.ilike(value)
|
||||
elif op == 'startswith': new_query = field.startswith(value)
|
||||
elif op == 'endswith': new_query = field.endswith(value)
|
||||
else: raise RuntimeError("Invalid operation")
|
||||
elif field._db._adapter.dbengine=='google:datastore' and \
|
||||
field.type in ('list:integer', 'list:string', 'list:reference'):
|
||||
if op == 'contains': new_query = field.contains(value)
|
||||
else: raise RuntimeError("Invalid operation")
|
||||
else: raise RuntimeError("Invalid operation")
|
||||
if neg: new_query = ~new_query
|
||||
if query is None:
|
||||
query = new_query
|
||||
elif logic == 'and':
|
||||
query &= new_query
|
||||
elif logic == 'or':
|
||||
query |= new_query
|
||||
field = op = neg = logic = None
|
||||
return query
|
||||
|
||||
|
||||
def sqlhtml_validators(field):
|
||||
"""
|
||||
Field type validation, using web2py's validators mechanism.
|
||||
|
||||
makes sure the content of a field is in line with the declared
|
||||
fieldtype
|
||||
"""
|
||||
db = field.db
|
||||
try:
|
||||
from gluon import validators
|
||||
except ImportError:
|
||||
return []
|
||||
field_type, field_length = field.type, field.length
|
||||
if isinstance(field_type, SQLCustomType):
|
||||
if hasattr(field_type, 'validator'):
|
||||
return field_type.validator
|
||||
else:
|
||||
field_type = field_type.type
|
||||
elif not isinstance(field_type,str):
|
||||
return []
|
||||
requires=[]
|
||||
def ff(r,id):
|
||||
row=r(id)
|
||||
if not row:
|
||||
return str(id)
|
||||
elif hasattr(r, '_format') and isinstance(r._format,str):
|
||||
return r._format % row
|
||||
elif hasattr(r, '_format') and callable(r._format):
|
||||
return r._format(row)
|
||||
else:
|
||||
return str(id)
|
||||
if field_type in (('string', 'text', 'password')):
|
||||
requires.append(validators.IS_LENGTH(field_length))
|
||||
elif field_type == 'json':
|
||||
requires.append(validators.IS_EMPTY_OR(validators.IS_JSON()))
|
||||
elif field_type == 'double' or field_type == 'float':
|
||||
requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100))
|
||||
elif field_type == 'integer':
|
||||
requires.append(validators.IS_INT_IN_RANGE(-2**31, 2**31))
|
||||
elif field_type == 'bigint':
|
||||
requires.append(validators.IS_INT_IN_RANGE(-2**63, 2**63))
|
||||
elif field_type.startswith('decimal'):
|
||||
requires.append(validators.IS_DECIMAL_IN_RANGE(-10**10, 10**10))
|
||||
elif field_type == 'date':
|
||||
requires.append(validators.IS_DATE())
|
||||
elif field_type == 'time':
|
||||
requires.append(validators.IS_TIME())
|
||||
elif field_type == 'datetime':
|
||||
requires.append(validators.IS_DATETIME())
|
||||
elif db and field_type.startswith('reference') and \
|
||||
field_type.find('.') < 0 and \
|
||||
field_type[10:] in db.tables:
|
||||
referenced = db[field_type[10:]]
|
||||
def repr_ref(id, row=None, r=referenced, f=ff): return f(r, id)
|
||||
field.represent = field.represent or repr_ref
|
||||
if hasattr(referenced, '_format') and referenced._format:
|
||||
requires = validators.IS_IN_DB(db,referenced._id,
|
||||
referenced._format)
|
||||
if field.unique:
|
||||
requires._and = validators.IS_NOT_IN_DB(db,field)
|
||||
if field.tablename == field_type[10:]:
|
||||
return validators.IS_EMPTY_OR(requires)
|
||||
return requires
|
||||
elif db and field_type.startswith('list:reference') and \
|
||||
field_type.find('.') < 0 and \
|
||||
field_type[15:] in db.tables:
|
||||
referenced = db[field_type[15:]]
|
||||
def list_ref_repr(ids, row=None, r=referenced, f=ff):
|
||||
if not ids:
|
||||
return None
|
||||
from ..adapters.google import GoogleDatastoreAdapter
|
||||
refs = None
|
||||
db, id = r._db, r._id
|
||||
if isinstance(db._adapter, GoogleDatastoreAdapter):
|
||||
def count(values): return db(id.belongs(values)).select(id)
|
||||
rx = range(0, len(ids), 30)
|
||||
refs = reduce(lambda a,b:a&b, [count(ids[i:i+30]) for i in rx])
|
||||
else:
|
||||
refs = db(id.belongs(ids)).select(id)
|
||||
return (refs and ', '.join(f(r,x.id) for x in refs) or '')
|
||||
field.represent = field.represent or list_ref_repr
|
||||
if hasattr(referenced, '_format') and referenced._format:
|
||||
requires = validators.IS_IN_DB(db,referenced._id,
|
||||
referenced._format,multiple=True)
|
||||
else:
|
||||
requires = validators.IS_IN_DB(db,referenced._id,
|
||||
multiple=True)
|
||||
if field.unique:
|
||||
requires._and = validators.IS_NOT_IN_DB(db,field)
|
||||
if not field.notnull:
|
||||
requires = validators.IS_EMPTY_OR(requires)
|
||||
return requires
|
||||
elif field_type.startswith('list:'):
|
||||
def repr_list(values,row=None): return', '.join(str(v) for v in (values or []))
|
||||
field.represent = field.represent or repr_list
|
||||
if field.unique:
|
||||
requires.append(validators.IS_NOT_IN_DB(db, field))
|
||||
sff = ['in', 'do', 'da', 'ti', 'de', 'bo']
|
||||
if field.notnull and not field_type[:2] in sff:
|
||||
requires.append(validators.IS_NOT_EMPTY())
|
||||
elif not field.notnull and field_type[:2] in sff and requires:
|
||||
requires[0] = validators.IS_EMPTY_OR(requires[0])
|
||||
return requires
|
||||
|
||||
|
||||
def varquote_aux(name,quotestr='%s'):
|
||||
return name if REGEX_W.match(name) else quotestr % name
|
||||
|
||||
|
||||
def uuid2int(uuidv):
|
||||
return uuid.UUID(uuidv).int
|
||||
|
||||
|
||||
def int2uuid(n):
|
||||
return str(uuid.UUID(int=n))
|
||||
|
||||
|
||||
# Geodal utils
|
||||
def geoPoint(x, y):
|
||||
return "POINT (%f %f)" % (x, y)
|
||||
|
||||
|
||||
def geoLine(*line):
|
||||
return "LINESTRING (%s)" % ','.join("%f %f" % item for item in line)
|
||||
|
||||
|
||||
def geoPolygon(*line):
|
||||
return "POLYGON ((%s))" % ','.join("%f %f" % item for item in line)
|
||||
@@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
|
||||
REGEX_TYPE = re.compile('^([\w\_\:]+)')
|
||||
REGEX_DBNAME = re.compile('^(\w+)(\:\w+)*')
|
||||
REGEX_W = re.compile('^\w+$')
|
||||
REGEX_TABLE_DOT_FIELD = re.compile('^(\w+)\.([^.]+)$')
|
||||
REGEX_NO_GREEDY_ENTITY_NAME = r'(.+?)'
|
||||
REGEX_UPLOAD_PATTERN = re.compile('(?P<table>[\w\-]+)\.(?P<field>[\w\-]+)\.(?P<uuidkey>[\w\-]+)(\.(?P<name>\w+))?\.\w+$')
|
||||
REGEX_CLEANUP_FN = re.compile('[\'"\s;]+')
|
||||
REGEX_UNPACK = re.compile('(?<!\|)\|(?!\|)')
|
||||
REGEX_PYTHON_KEYWORDS = re.compile('^(and|del|from|not|while|as|elif|global|or|with|assert|else|if|pass|yield|break|except|import|print|class|exec|in|raise|continue|finally|is|return|def|for|lambda|try)$')
|
||||
REGEX_SELECT_AS_PARSER = re.compile("\s+AS\s+(\S+)")
|
||||
REGEX_CONST_STRING = re.compile('(\"[^\"]*?\")|(\'[^\']*?\')')
|
||||
REGEX_SEARCH_PATTERN = re.compile('^{[^\.]+\.[^\.]+(\.(lt|gt|le|ge|eq|ne|contains|startswith|year|month|day|hour|minute|second))?(\.not)?}$')
|
||||
REGEX_SQUARE_BRACKETS = re.compile('^.+\[.+\]$')
|
||||
REGEX_STORE_PATTERN = re.compile('\.(?P<e>\w{1,5})$')
|
||||
REGEX_QUOTES = re.compile("'[^']*'")
|
||||
REGEX_ALPHANUMERIC = re.compile('^[0-9a-zA-Z]\w*$')
|
||||
REGEX_PASSWORD = re.compile('\://([^:@]*)\:')
|
||||
REGEX_NOPASSWD = re.compile('\/\/[\w\.\-]+[\:\/](.+)(?=@)') # was '(?<=[\:\/])([^:@/]+)(?=@.+)'
|
||||
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -94,7 +94,7 @@ def parse_version(version):
|
||||
return version_tuple
|
||||
|
||||
def read_file(filename, mode='r'):
|
||||
"""Returns content from filename, making sure to close the file explicitly
|
||||
"""Returns content from filename, making sure to close the file explicitly
|
||||
on exit.
|
||||
"""
|
||||
f = open(filename, mode)
|
||||
@@ -105,7 +105,7 @@ def read_file(filename, mode='r'):
|
||||
|
||||
|
||||
def write_file(filename, value, mode='w'):
|
||||
"""Writes <value> to filename, making sure to close the file
|
||||
"""Writes <value> to filename, making sure to close the file
|
||||
explicitly on exit.
|
||||
"""
|
||||
f = open(filename, mode)
|
||||
@@ -346,7 +346,7 @@ def get_session(request, other_application='admin'):
|
||||
session_filename = os.path.join(
|
||||
up(request.folder), other_application, 'sessions', session_id)
|
||||
if not os.path.exists(session_filename):
|
||||
session_filename = generate(session_filename)
|
||||
session_filename = generate(session_filename)
|
||||
osession = storage.load_storage(session_filename)
|
||||
except Exception, e:
|
||||
osession = storage.Storage()
|
||||
@@ -438,7 +438,7 @@ from settings import global_settings # we need to import settings here because
|
||||
|
||||
|
||||
def abspath(*relpath, **base):
|
||||
"""Converts relative path to absolute path based (by default) on
|
||||
"""Converts relative path to absolute path based (by default) on
|
||||
applications_parent
|
||||
"""
|
||||
path = os.path.join(*relpath)
|
||||
|
||||
+6
-1
@@ -25,7 +25,6 @@ from gluon.serializers import json, custom_json
|
||||
import gluon.settings as settings
|
||||
from gluon.utils import web2py_uuid, secure_dumps, secure_loads
|
||||
from gluon.settings import global_settings
|
||||
from gluon.dal import Field
|
||||
from gluon import recfile
|
||||
import hashlib
|
||||
import portalocker
|
||||
@@ -598,6 +597,7 @@ class Response(Storage):
|
||||
|
||||
Downloads from http://..../download/filename
|
||||
"""
|
||||
from pydal.exceptions import NotAuthorizedException, NotFoundException
|
||||
|
||||
current.session.forget(current.response)
|
||||
|
||||
@@ -614,6 +614,10 @@ class Response(Storage):
|
||||
raise HTTP(404)
|
||||
try:
|
||||
(filename, stream) = field.retrieve(name, nameonly=True)
|
||||
except NotAuthorizedException:
|
||||
raise HTTP(403)
|
||||
except NotFoundException:
|
||||
raise HTTP(404)
|
||||
except IOError:
|
||||
raise HTTP(404)
|
||||
headers = self.headers
|
||||
@@ -769,6 +773,7 @@ class Session(Storage):
|
||||
compression_level(int): 0-9, sets zlib compression on the data
|
||||
before the encryption
|
||||
"""
|
||||
from gluon.dal import Field
|
||||
request = request or current.request
|
||||
response = response or current.response
|
||||
masterapp = masterapp or request.application
|
||||
|
||||
+2
-2
@@ -850,7 +850,7 @@ class DIV(XmlComponent):
|
||||
"""
|
||||
components = []
|
||||
for c in self.components:
|
||||
if isinstance(c, allowed_parents):
|
||||
if isinstance(c, (allowed_parents,CAT)):
|
||||
pass
|
||||
elif wrap_lambda:
|
||||
c = wrap_lambda(c)
|
||||
@@ -1864,7 +1864,7 @@ class INPUT(DIV):
|
||||
break
|
||||
if not name in self.errors:
|
||||
self.vars[name] = value
|
||||
return True
|
||||
return True
|
||||
return False
|
||||
|
||||
def _postprocessing(self):
|
||||
|
||||
+3
-3
@@ -93,7 +93,7 @@ from gluon.globals import Request, Response, Session
|
||||
from gluon.compileapp import build_environment, run_models_in, \
|
||||
run_controller_in, run_view_in
|
||||
from gluon.contenttype import contenttype
|
||||
from gluon.dal.base import BaseAdapter
|
||||
from pydal.base import BaseAdapter
|
||||
from gluon.validators import CRYPT
|
||||
from gluon.html import URL, xmlescape
|
||||
from gluon.utils import is_valid_ip_address, getipaddrinfo
|
||||
@@ -365,8 +365,8 @@ def wsgibase(environ, responder):
|
||||
client = client,
|
||||
folder = abspath('applications', app) + os.sep,
|
||||
ajax = x_req_with == 'xmlhttprequest',
|
||||
cid = env.http_web2py_component_element,
|
||||
is_local = (env.remote_addr in local_hosts and
|
||||
cid = env.http_web2py_component_element,
|
||||
is_local = (env.remote_addr in local_hosts and
|
||||
client == env.remote_addr),
|
||||
is_shell = cmd_opts and cmd_opts.shell,
|
||||
is_sheduler = cmd_opts and cmd_opts.scheduler,
|
||||
|
||||
Submodule
+1
Submodule gluon/packages/dal added at b533d6a7a8
+63
-63
@@ -1,63 +1,63 @@
|
||||
import os, uuid
|
||||
|
||||
def generate(filename, depth=2, base=512):
|
||||
if os.path.sep in filename:
|
||||
path, filename = os.path.split(filename)
|
||||
else:
|
||||
path = None
|
||||
dummyhash = sum(ord(c)*256**(i % 4) for i,c in enumerate(filename)) % base**depth
|
||||
folders = []
|
||||
for level in range(depth-1,-1,-1):
|
||||
code, dummyhash = divmod(dummyhash, base**level)
|
||||
folders.append("%03x" % code)
|
||||
folders.append(filename)
|
||||
if path:
|
||||
folders.insert(0,path)
|
||||
return os.path.join(*folders)
|
||||
|
||||
def exists(filename, path=None):
|
||||
if os.path.exists(filename):
|
||||
return True
|
||||
if path is None:
|
||||
path, filename = os.path.split(filename)
|
||||
fullfilename = os.path.join(path, generate(filename))
|
||||
if os.path.exists(fullfilename):
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove(filename, path=None):
|
||||
if os.path.exists(filename):
|
||||
return os.unlink(filename)
|
||||
if path is None:
|
||||
path, filename = os.path.split(filename)
|
||||
fullfilename = os.path.join(path, generate(filename))
|
||||
if os.path.exists(fullfilename):
|
||||
return os.unlink(fullfilename)
|
||||
raise IOError
|
||||
|
||||
def open(filename, mode="r", path=None):
|
||||
if not path:
|
||||
path, filename = os.path.split(filename)
|
||||
fullfilename = None
|
||||
if not mode.startswith('w'):
|
||||
fullfilename = os.path.join(path, filename)
|
||||
if not os.path.exists(fullfilename):
|
||||
fullfilename = None
|
||||
if not fullfilename:
|
||||
fullfilename = os.path.join(path, generate(filename))
|
||||
if mode.startswith('w') and not os.path.exists(os.path.dirname(fullfilename)):
|
||||
os.makedirs(os.path.dirname(fullfilename))
|
||||
return file(fullfilename, mode)
|
||||
|
||||
def test():
|
||||
if not os.path.exists('tests'):
|
||||
os.mkdir('tests')
|
||||
for k in range(20):
|
||||
filename = os.path.join('tests',str(uuid.uuid4())+'.test')
|
||||
open(filename, "w").write('test')
|
||||
assert open(filename, "r").read()=='test'
|
||||
if exists(filename):
|
||||
remove(filename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
import os, uuid
|
||||
|
||||
def generate(filename, depth=2, base=512):
|
||||
if os.path.sep in filename:
|
||||
path, filename = os.path.split(filename)
|
||||
else:
|
||||
path = None
|
||||
dummyhash = sum(ord(c)*256**(i % 4) for i,c in enumerate(filename)) % base**depth
|
||||
folders = []
|
||||
for level in range(depth-1,-1,-1):
|
||||
code, dummyhash = divmod(dummyhash, base**level)
|
||||
folders.append("%03x" % code)
|
||||
folders.append(filename)
|
||||
if path:
|
||||
folders.insert(0,path)
|
||||
return os.path.join(*folders)
|
||||
|
||||
def exists(filename, path=None):
|
||||
if os.path.exists(filename):
|
||||
return True
|
||||
if path is None:
|
||||
path, filename = os.path.split(filename)
|
||||
fullfilename = os.path.join(path, generate(filename))
|
||||
if os.path.exists(fullfilename):
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove(filename, path=None):
|
||||
if os.path.exists(filename):
|
||||
return os.unlink(filename)
|
||||
if path is None:
|
||||
path, filename = os.path.split(filename)
|
||||
fullfilename = os.path.join(path, generate(filename))
|
||||
if os.path.exists(fullfilename):
|
||||
return os.unlink(fullfilename)
|
||||
raise IOError
|
||||
|
||||
def open(filename, mode="r", path=None):
|
||||
if not path:
|
||||
path, filename = os.path.split(filename)
|
||||
fullfilename = None
|
||||
if not mode.startswith('w'):
|
||||
fullfilename = os.path.join(path, filename)
|
||||
if not os.path.exists(fullfilename):
|
||||
fullfilename = None
|
||||
if not fullfilename:
|
||||
fullfilename = os.path.join(path, generate(filename))
|
||||
if mode.startswith('w') and not os.path.exists(os.path.dirname(fullfilename)):
|
||||
os.makedirs(os.path.dirname(fullfilename))
|
||||
return file(fullfilename, mode)
|
||||
|
||||
def test():
|
||||
if not os.path.exists('tests'):
|
||||
os.mkdir('tests')
|
||||
for k in range(20):
|
||||
filename = os.path.join('tests',str(uuid.uuid4())+'.test')
|
||||
open(filename, "w").write('test')
|
||||
assert open(filename, "r").read()=='test'
|
||||
if exists(filename):
|
||||
remove(filename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
||||
+2
-2
@@ -609,7 +609,7 @@ class Scheduler(MetaScheduler):
|
||||
|
||||
def define_tables(self, db, migrate):
|
||||
"""Defines Scheduler tables structure"""
|
||||
from gluon.dal.base import DEFAULT
|
||||
from pydal.base import DEFAULT
|
||||
logger.debug('defining tables (migrate=%s)', migrate)
|
||||
now = self.now
|
||||
db.define_table(
|
||||
@@ -1330,7 +1330,7 @@ class Scheduler(MetaScheduler):
|
||||
have all fields == None
|
||||
|
||||
"""
|
||||
from gluon.dal.objects import Query
|
||||
from pydal.objects import Query
|
||||
sr, st = self.db.scheduler_run, self.db.scheduler_task
|
||||
if isinstance(ref, (int, long)):
|
||||
q = st.id == ref
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ from gluon.restricted import RestrictedError
|
||||
from gluon.globals import Request, Response, Session
|
||||
from gluon.storage import Storage, List
|
||||
from gluon.admin import w2p_unpack
|
||||
from gluon.dal.base import BaseAdapter
|
||||
from pydal.base import BaseAdapter
|
||||
|
||||
logger = logging.getLogger("web2py")
|
||||
|
||||
@@ -129,7 +129,7 @@ def env(
|
||||
request.function)
|
||||
if global_settings.cmd_options:
|
||||
ip = global_settings.cmd_options.ip
|
||||
port = global_settings.cmd_options.port
|
||||
port = global_settings.cmd_options.port
|
||||
else:
|
||||
ip, port = '127.0.0.1', '8000'
|
||||
request.env.http_host = '%s:%s' % (ip,port)
|
||||
|
||||
+4
-3
@@ -13,9 +13,10 @@ Just for backward compatibility
|
||||
__all__ = ['DAL', 'Field', 'DRIVERS']
|
||||
|
||||
from dal import DAL, Field, SQLCustomType
|
||||
from dal.adapters.base import BaseAdapter, DRIVERS
|
||||
from dal.objects import Table, Query, Set, Expression, Row, Rows
|
||||
from dal.helpers.classes import SQLALL
|
||||
from pydal.base import BaseAdapter
|
||||
from pydal.drivers import DRIVERS
|
||||
from pydal.objects import Table, Query, Set, Expression, Row, Rows
|
||||
from pydal.helpers.classes import SQLALL
|
||||
|
||||
SQLDB = DAL
|
||||
GQLDB = DAL
|
||||
|
||||
+23
-14
@@ -26,12 +26,12 @@ from gluon.html import XML, SPAN, TAG, A, DIV, CAT, UL, LI, TEXTAREA, BR, IMG
|
||||
from gluon.html import FORM, INPUT, LABEL, OPTION, SELECT, COL, COLGROUP
|
||||
from gluon.html import TABLE, THEAD, TBODY, TR, TD, TH, STYLE, SCRIPT
|
||||
from gluon.html import URL, FIELDSET, P, DEFAULT_PASSWORD_DISPLAY
|
||||
from gluon.dal import DAL, Field
|
||||
from gluon.dal.base import DEFAULT
|
||||
from gluon.dal.objects import Table, Row, Expression
|
||||
from gluon.dal.adapters.base import CALLABLETYPES
|
||||
from gluon.dal.helpers.methods import smart_query, bar_encode, sqlhtml_validators
|
||||
from gluon.dal.helpers.classes import Reference, SQLCustomType
|
||||
from pydal.base import DEFAULT
|
||||
from pydal.objects import Table, Row, Expression, Field
|
||||
from pydal.adapters.base import CALLABLETYPES
|
||||
from pydal.helpers.methods import smart_query, bar_encode
|
||||
from pydal.helpers.classes import Reference, SQLCustomType
|
||||
from gluon.dal import _default_validators
|
||||
from gluon.storage import Storage
|
||||
from gluon.utils import md5_hash
|
||||
from gluon.validators import IS_EMPTY_OR, IS_NOT_EMPTY, IS_LIST_OF, IS_DATE
|
||||
@@ -1127,7 +1127,8 @@ class SQLFORM(FORM):
|
||||
extra_field.table = table
|
||||
extra_field.tablename = table._tablename
|
||||
if extra_field.requires == DEFAULT:
|
||||
extra_field.requires = sqlhtml_validators(extra_field)
|
||||
extra_field.requires = _default_validators(table._db,
|
||||
extra_field)
|
||||
|
||||
for fieldname in self.fields:
|
||||
if fieldname.find('.') >= 0:
|
||||
@@ -1722,6 +1723,8 @@ class SQLFORM(FORM):
|
||||
Internally will build a non-database based data model
|
||||
to hold the fields.
|
||||
"""
|
||||
# this is here to avoid circular references
|
||||
from gluon.dal import DAL
|
||||
# Define a table name, this way it can be logical to our CSS.
|
||||
# And if you switch from using SQLFORM to SQLFORM.factory
|
||||
# your same css definitions will still apply.
|
||||
@@ -2332,8 +2335,12 @@ class SQLFORM(FORM):
|
||||
#fields but not virtual fields
|
||||
sfields = reduce(lambda a, b: a + b,
|
||||
[[f for f in t if f.readable and not isinstance(f, Field.Virtual)] for t in tables])
|
||||
dbset = dbset(SQLFORM.build_query(
|
||||
sfields, keywords))
|
||||
#use custom_query using searchable
|
||||
if callable(searchable):
|
||||
dbset = dbset(searchable(sfields, keywords))
|
||||
else:
|
||||
dbset = dbset(SQLFORM.build_query(
|
||||
sfields, keywords))
|
||||
rows = dbset.select(left=left, orderby=orderby,
|
||||
cacheable=True, *selectable_columns)
|
||||
except Exception, e:
|
||||
@@ -2348,7 +2355,9 @@ class SQLFORM(FORM):
|
||||
# expcolumns is all cols to be exported including virtual fields
|
||||
rows.colnames = expcolumns
|
||||
oExp = clazz(rows)
|
||||
filename = '.'.join(('rows', oExp.file_ext))
|
||||
export_filename = \
|
||||
request.vars.get('_export_filename') or 'rows'
|
||||
filename = '.'.join((export_filename, oExp.file_ext))
|
||||
response.headers['Content-Type'] = oExp.content_type
|
||||
response.headers['Content-Disposition'] = \
|
||||
'attachment;filename=' + filename + ';'
|
||||
@@ -3190,13 +3199,13 @@ class SQLTABLE(TABLE):
|
||||
r = ''
|
||||
elif field.type in ['string', 'text']:
|
||||
r = str(field.formatter(r))
|
||||
truncate_by = truncate
|
||||
if headers != {}: # new implement dict
|
||||
if isinstance(headers[colname], dict):
|
||||
if isinstance(headers[colname]['truncate'], int):
|
||||
r = truncate_string(
|
||||
r, headers[colname]['truncate'])
|
||||
elif not truncate is None:
|
||||
r = truncate_string(r, truncate)
|
||||
truncate_by = headers[colname]['truncate']
|
||||
if not truncate_by is None:
|
||||
r = truncate_string(r, truncate_by)
|
||||
attrcol = dict() # new implement dict
|
||||
if headers != {}:
|
||||
if isinstance(headers[colname], dict):
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@ class Messages(Settings):
|
||||
def __getattr__(self, key):
|
||||
value = self[key]
|
||||
if isinstance(value, str):
|
||||
return str(self.T(value))
|
||||
return self.T(value)
|
||||
return value
|
||||
|
||||
class FastStorage(dict):
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ class TemplateParser(object):
|
||||
self.context = context
|
||||
|
||||
# allow optional alternative delimiters
|
||||
|
||||
|
||||
if delimiters != self.default_delimiters:
|
||||
escaped_delimiters = (escape(delimiters[0]),
|
||||
escape(delimiters[1]))
|
||||
|
||||
+3
-11
@@ -1,4 +1,4 @@
|
||||
import os, sys
|
||||
import sys
|
||||
|
||||
from test_http import *
|
||||
from test_cache import *
|
||||
@@ -16,16 +16,8 @@ from test_validators import *
|
||||
from test_utils import *
|
||||
from test_contribs import *
|
||||
from test_web import *
|
||||
|
||||
from test_dal import *
|
||||
from test_tools import *
|
||||
|
||||
if sys.version[:3] == '2.7':
|
||||
from test_old_doctests import *
|
||||
|
||||
|
||||
NOSQL = any([name in (os.getenv("DB") or "")
|
||||
for name in ("datastore", "mongodb", "imap")])
|
||||
|
||||
if NOSQL:
|
||||
from test_dal_nosql import *
|
||||
else:
|
||||
from test_dal import *
|
||||
@@ -27,4 +27,4 @@ def fix_sys_path(current_path):
|
||||
os.path.abspath(os.path.join(path, 'site-packages')),
|
||||
os.path.abspath(os.path.join(path, 'gluon')),
|
||||
'']
|
||||
[add_path_first(path) for path in paths]
|
||||
[add_path_first(path) for path in paths]
|
||||
|
||||
@@ -65,7 +65,7 @@ class TestContribs(unittest.TestCase):
|
||||
self.assertEqual(myappconfig.take('config3.key1', cast=int), '1')
|
||||
|
||||
self.assertEqual(myappconfig.take('config3.key2'), 2)
|
||||
|
||||
|
||||
current.request = {}
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
+12
-1650
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22
-22
@@ -131,7 +131,7 @@ class TestRouter(unittest.TestCase):
|
||||
'http://domain.com/abc/def'), "/init/default/abc ['def']")
|
||||
self.assertEqual(filter_url(
|
||||
'http://domain.com/index/a%20bc'), "/init/default/index ['a bc']")
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/static" % root))
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
|
||||
try:
|
||||
@@ -195,7 +195,7 @@ class TestRouter(unittest.TestCase):
|
||||
norm_root('%s/applications/welcome/static/favicon.ico' % root))
|
||||
self.assertEqual(filter_url('http://domain.com/static/abc'),
|
||||
norm_root('%s/applications/welcome/static/abc' % root))
|
||||
self.assertEqual(filter_url('http://domain.com/static/path/to/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/static/path/to/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/static" % root))
|
||||
# outgoing
|
||||
self.assertEqual(filter_url(
|
||||
@@ -1086,13 +1086,13 @@ class TestRouter(unittest.TestCase):
|
||||
norm_root("%s/applications/admin/static/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/en/static/file'),
|
||||
norm_root("%s/applications/admin/static/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
|
||||
norm_root("%s/applications/examples/static/en/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/file'),
|
||||
norm_root("%s/applications/examples/static/en/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
|
||||
norm_root("%s/applications/examples/static/it/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
|
||||
norm_root("%s/applications/examples/static/file" % root))
|
||||
|
||||
self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn',
|
||||
@@ -1188,19 +1188,19 @@ class TestRouter(unittest.TestCase):
|
||||
norm_root("%s/applications/admin/static/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/en/static/file'),
|
||||
norm_root("%s/applications/admin/static/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
|
||||
norm_root("%s/applications/examples/static/en/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/file'),
|
||||
norm_root("%s/applications/examples/static/en/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
|
||||
norm_root("%s/applications/examples/static/it/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
|
||||
norm_root("%s/applications/examples/static/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/en/file').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/en/file').replace('/', os.sep),
|
||||
norm_root("%s/applications/examples/static/en/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/it/file').replace('/', os.sep),
|
||||
norm_root("%s/applications/examples/static/it/file" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/it-it/file').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/examples/static/it-it/file').replace('/', os.sep),
|
||||
norm_root("%s/applications/examples/static/it-it/file" % root))
|
||||
|
||||
def test_router_get_effective(self):
|
||||
@@ -1267,14 +1267,14 @@ class TestRouter(unittest.TestCase):
|
||||
|
||||
'''
|
||||
load(rdict=dict())
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/static" % root))
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to--/static" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/==to--/static" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/-+=@$%%/static" % root))
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/.static')
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/s..tatic')
|
||||
@@ -1287,7 +1287,7 @@ class TestRouter(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
load(rdict=router_static)
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/#static" % root))
|
||||
|
||||
router_static = dict(
|
||||
@@ -1296,23 +1296,23 @@ class TestRouter(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
load(rdict=router_static)
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/static" % root))
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to--/static" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/==to--/static" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/-+=@$%%/static" % root))
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to//static')
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/#static" % root))
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/./static')
|
||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/../static')
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/.../static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/.../static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/.../static" % root))
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/.static').replace('/', os.sep),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/.static').replace('/', os.sep),
|
||||
norm_root("%s/applications/welcome/static/path/to/.static" % root))
|
||||
|
||||
def test_router_args(self):
|
||||
|
||||
@@ -108,7 +108,7 @@ class TestRoutes(unittest.TestCase):
|
||||
'http://domain.com/abc/def/ghi/jkl'), "/abc/def/ghi ['jkl']")
|
||||
self.assertEqual(filter_url(
|
||||
'http://domain.com/abc/def/ghi/j%20kl'), "/abc/def/ghi ['j_kl']")
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'),
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'),
|
||||
norm_root("%s/applications/welcome/static/path/to/static" % root))
|
||||
# no more necessary since explcit check for directory traversal attacks
|
||||
"""
|
||||
@@ -174,7 +174,7 @@ default_application = 'defapp'
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/default/index/abc'), "/welcome/default/index ['abc']")
|
||||
self.assertEqual(filter_url('http://domain.com/welcome/static/abc'),
|
||||
norm_root('%s/applications/welcome/static/abc' % root))
|
||||
self.assertEqual(filter_url('http://domain.com/defapp/static/path/to/static'),
|
||||
self.assertEqual(filter_url('http://domain.com/defapp/static/path/to/static'),
|
||||
norm_root("%s/applications/defapp/static/path/to/static" % root))
|
||||
|
||||
def test_routes_raise(self):
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Unit tests for gluon.tools
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
if sys.version < "2.7":
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
from fix_path import fix_sys_path
|
||||
|
||||
fix_sys_path(__file__)
|
||||
|
||||
DEFAULT_URI = os.getenv('DB', 'sqlite:memory')
|
||||
|
||||
from gluon.dal import DAL, Field
|
||||
from pydal.objects import Table
|
||||
from tools import Auth
|
||||
from gluon.globals import Request, Response, Session
|
||||
from storage import Storage
|
||||
from languages import translator
|
||||
from gluon.http import HTTP
|
||||
|
||||
python_version = sys.version[:3]
|
||||
IS_IMAP = "imap" in DEFAULT_URI
|
||||
|
||||
@unittest.skipIf(IS_IMAP, "TODO: Imap raises 'Connection refused'")
|
||||
class testAuth(unittest.TestCase):
|
||||
|
||||
def testRun(self):
|
||||
# setup
|
||||
request = Request(env={})
|
||||
request.application = 'a'
|
||||
request.controller = 'c'
|
||||
request.function = 'f'
|
||||
request.folder = 'applications/admin'
|
||||
response = Response()
|
||||
session = Session()
|
||||
T = translator('', 'en')
|
||||
session.connect(request, response)
|
||||
from gluon.globals import current
|
||||
current.request = request
|
||||
current.response = response
|
||||
current.session = session
|
||||
current.T = T
|
||||
db = DAL(DEFAULT_URI, check_reserved=['all'])
|
||||
auth = Auth(db)
|
||||
auth.define_tables(username=True, signature=False)
|
||||
self.assertTrue('auth_user' in db)
|
||||
self.assertTrue('auth_group' in db)
|
||||
self.assertTrue('auth_membership' in db)
|
||||
self.assertTrue('auth_permission' in db)
|
||||
self.assertTrue('auth_event' in db)
|
||||
db.define_table('t0', Field('tt'), auth.signature)
|
||||
auth.enable_record_versioning(db)
|
||||
self.assertTrue('t0_archive' in db)
|
||||
for f in ['login', 'register', 'retrieve_password',
|
||||
'retrieve_username']:
|
||||
html_form = getattr(auth, f)().xml()
|
||||
self.assertTrue('name="_formkey"' in html_form)
|
||||
|
||||
for f in ['logout', 'verify_email', 'reset_password',
|
||||
'change_password', 'profile', 'groups']:
|
||||
self.assertRaisesRegexp(HTTP, "303*", getattr(auth, f))
|
||||
|
||||
self.assertRaisesRegexp(HTTP, "401*", auth.impersonate)
|
||||
|
||||
try:
|
||||
for t in ['t0_archive', 't0', 'auth_cas', 'auth_event',
|
||||
'auth_membership', 'auth_permission', 'auth_group',
|
||||
'auth_user']:
|
||||
db[t].drop()
|
||||
except SyntaxError as e:
|
||||
# GAE doesn't support drop
|
||||
pass
|
||||
return
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+46
-37
@@ -43,7 +43,7 @@ from gluon import *
|
||||
from gluon.contrib.autolinks import expand_one
|
||||
from gluon.contrib.markmin.markmin2html import \
|
||||
replace_at_urls, replace_autolinks, replace_components
|
||||
from gluon.dal.objects import Table, Row, Set, Query
|
||||
from pydal.objects import Table, Row, Set, Query
|
||||
|
||||
import gluon.serializers as serializers
|
||||
|
||||
@@ -136,6 +136,7 @@ class Mail(object):
|
||||
mail.settings.server
|
||||
mail.settings.sender
|
||||
mail.settings.login
|
||||
mail.settings.timeout = 60 # seconds (default)
|
||||
|
||||
When server is 'logging', email is logged but not sent (debug mode)
|
||||
|
||||
@@ -172,7 +173,7 @@ class Mail(object):
|
||||
chain of email certificate. It can be a
|
||||
string containing the certs to. (PEM format)
|
||||
x509_nocerts : if True then no attached certificate in mail
|
||||
x509_crypt_certfiles: the certificates file or strings to encrypt
|
||||
x509_crypt_certfiles: the certificates file or strings to encrypt
|
||||
the messages with can be a file name /
|
||||
string or a list of file names /
|
||||
strings (PEM format)
|
||||
@@ -265,6 +266,7 @@ class Mail(object):
|
||||
settings.sender = sender
|
||||
settings.login = login
|
||||
settings.tls = tls
|
||||
settings.timeout = 60 # seconds
|
||||
settings.hostname = None
|
||||
settings.ssl = False
|
||||
settings.cipher_type = None
|
||||
@@ -340,7 +342,7 @@ class Mail(object):
|
||||
from_address: address to appear in the 'From:' header, this is not
|
||||
the envelope sender. If not specified the sender will be used
|
||||
|
||||
cipher_type :
|
||||
cipher_type :
|
||||
gpg - need a python-pyme package and gpgme lib
|
||||
x509 - smime
|
||||
gpg_home : you can set a GNUPGHOME environment variable
|
||||
@@ -359,8 +361,8 @@ class Mail(object):
|
||||
chain of email certificate. It can be a
|
||||
string containing the certs to. (PEM format)
|
||||
x509_nocerts : if True then no attached certificate in mail
|
||||
x509_crypt_certfiles: the certificates file or strings to encrypt
|
||||
the messages with can be a file name / string or
|
||||
x509_crypt_certfiles: the certificates file or strings to encrypt
|
||||
the messages with can be a file name / string or
|
||||
a list of file names / strings (PEM format)
|
||||
Examples:
|
||||
Send plain text message to single address::
|
||||
@@ -787,10 +789,11 @@ class Mail(object):
|
||||
subject=subject, body=text, **xcc)
|
||||
else:
|
||||
smtp_args = self.settings.server.split(':')
|
||||
kwargs = dict(timeout = self.settings.timeout)
|
||||
if self.settings.ssl:
|
||||
server = smtplib.SMTP_SSL(*smtp_args)
|
||||
server = smtplib.SMTP_SSL(*smtp_args, **kwargs)
|
||||
else:
|
||||
server = smtplib.SMTP(*smtp_args)
|
||||
server = smtplib.SMTP(*smtp_args, **kwargs)
|
||||
if self.settings.tls and not self.settings.ssl:
|
||||
server.ehlo(self.settings.hostname)
|
||||
server.starttls()
|
||||
@@ -842,6 +845,7 @@ class Recaptcha(DIV):
|
||||
comment = '',
|
||||
ajax=False
|
||||
):
|
||||
request = request or current.request
|
||||
self.request_vars = request and request.vars or current.request.vars
|
||||
self.remote_addr = request.env.remote_addr
|
||||
self.public_key = public_key
|
||||
@@ -1257,7 +1261,8 @@ class Auth(object):
|
||||
def __init__(self, environment=None, db=None, mailer=True,
|
||||
hmac_key=None, controller='default', function='user',
|
||||
cas_provider=None, signature=True, secure=False,
|
||||
csrf_prevention=True, propagate_extension=None):
|
||||
csrf_prevention=True, propagate_extension=None,
|
||||
url_index=None):
|
||||
|
||||
## next two lines for backward compatibility
|
||||
if not db and environment and isinstance(environment, DAL):
|
||||
@@ -1294,7 +1299,7 @@ class Auth(object):
|
||||
del session.auth
|
||||
# ## what happens after login?
|
||||
|
||||
url_index = URL(controller, 'index')
|
||||
url_index = url_index or URL(controller, 'index')
|
||||
url_login = URL(controller, function, args='login',
|
||||
extension = propagate_extension)
|
||||
# ## what happens after registration?
|
||||
@@ -1346,7 +1351,7 @@ class Auth(object):
|
||||
reset_password_onvalidation = [],
|
||||
reset_password_onaccept = [],
|
||||
hmac_key = hmac_key,
|
||||
formstyle = current.response.formstyle,
|
||||
formstyle = current.response.formstyle,
|
||||
)
|
||||
settings.lock_keys = True
|
||||
|
||||
@@ -2340,7 +2345,7 @@ class Auth(object):
|
||||
items = snext.split('/')
|
||||
if '//' in snext and items[2] != request.env.http_host:
|
||||
snext = None
|
||||
|
||||
|
||||
if snext:
|
||||
session._auth_next = snext
|
||||
elif session._auth_next:
|
||||
@@ -2428,20 +2433,20 @@ class Auth(object):
|
||||
separator=settings.label_separator,
|
||||
extra_fields = extra_fields,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
captcha = settings.login_captcha or \
|
||||
(settings.login_captcha != False and settings.captcha)
|
||||
if captcha:
|
||||
addrow(form, captcha.label, captcha, captcha.comment,
|
||||
settings.formstyle, 'captcha__row')
|
||||
accepted_form = False
|
||||
|
||||
|
||||
if form.accepts(request, session if self.csrf_prevention else None,
|
||||
formname='login', dbio=False,
|
||||
onvalidation=onvalidation,
|
||||
hideerror=settings.hideerror):
|
||||
|
||||
|
||||
accepted_form = True
|
||||
# check for username in db
|
||||
entered_username = form.vars[username]
|
||||
@@ -2459,7 +2464,7 @@ class Auth(object):
|
||||
elif temp_user.registration_key in ('disabled', 'blocked'):
|
||||
response.flash = self.messages.login_disabled
|
||||
return form
|
||||
elif (not temp_user.registration_key is None
|
||||
elif (not temp_user.registration_key is None
|
||||
and temp_user.registration_key.strip()):
|
||||
response.flash = \
|
||||
self.messages.registration_verifying
|
||||
@@ -2507,11 +2512,11 @@ class Auth(object):
|
||||
redirect(
|
||||
self.url(args=request.args, vars=request.get_vars),
|
||||
client_side=settings.client_side)
|
||||
|
||||
|
||||
else: # use a central authentication server
|
||||
cas = settings.login_form
|
||||
cas_user = cas.get_user()
|
||||
|
||||
|
||||
if cas_user:
|
||||
cas_user[passfield] = None
|
||||
user = self.get_or_create_user(
|
||||
@@ -2527,7 +2532,7 @@ class Auth(object):
|
||||
|
||||
# Extra login logic for two-factor authentication
|
||||
#################################################
|
||||
# If the 'user' variable has a value, this means that the first
|
||||
# If the 'user' variable has a value, this means that the first
|
||||
# authentication step was successful (i.e. user provided correct
|
||||
# username and password at the first challenge).
|
||||
# Check if this user is signed up for two-factor authentication
|
||||
@@ -2540,7 +2545,7 @@ class Auth(object):
|
||||
if session.auth_two_factor_enabled:
|
||||
form = SQLFORM.factory(
|
||||
Field('authentication_code',
|
||||
required=True,
|
||||
required=True,
|
||||
comment='This code was emailed to you and is required for login.'),
|
||||
hidden=dict(_next=next),
|
||||
formstyle=settings.formstyle,
|
||||
@@ -2559,8 +2564,8 @@ class Auth(object):
|
||||
session.auth_two_factor_tries_left = 3 # Allow user to try up to 4 times
|
||||
# TODO: Add some error checking to handle cases where email cannot be sent
|
||||
self.settings.mailer.send(
|
||||
to=user.email,
|
||||
subject="Two-step Login Authentication Code",
|
||||
to=user.email,
|
||||
subject="Two-step Login Authentication Code",
|
||||
message="Your temporary login code is {0}".format(session.auth_two_factor))
|
||||
if form.accepts(request, session if self.csrf_prevention else None,
|
||||
formname='login', dbio=False,
|
||||
@@ -2575,15 +2580,15 @@ class Auth(object):
|
||||
# normal.
|
||||
if user is None or user == session.auth_two_factor_user:
|
||||
user = session.auth_two_factor_user
|
||||
# For security, because the username stored in the
|
||||
# For security, because the username stored in the
|
||||
# session somehow does not match the just validated
|
||||
# user. Should not be possible without session stealing
|
||||
# which is hard with SSL.
|
||||
elif user != session.auth_two_factor_user:
|
||||
user = None
|
||||
# Either way, the user and code associated with this session should
|
||||
# be removed. This handles cases where the session login may have
|
||||
# expired but browser window is open, so the old session key and
|
||||
# be removed. This handles cases where the session login may have
|
||||
# expired but browser window is open, so the old session key and
|
||||
# session usernamem will still exist
|
||||
self._reset_two_factor_auth(session)
|
||||
else:
|
||||
@@ -2631,11 +2636,11 @@ class Auth(object):
|
||||
"""
|
||||
Logouts and redirects to login
|
||||
"""
|
||||
|
||||
|
||||
# Clear out 2-step authentication information if user logs
|
||||
# out. This information is also cleared on successful login.
|
||||
self._reset_two_factor_auth(current.session)
|
||||
|
||||
|
||||
if next is DEFAULT:
|
||||
next = self.get_vars_next() or self.settings.logout_next
|
||||
if onlogout is DEFAULT:
|
||||
@@ -2786,7 +2791,7 @@ class Auth(object):
|
||||
else:
|
||||
next = replace_id(next, form)
|
||||
redirect(next, client_side=self.settings.client_side)
|
||||
|
||||
|
||||
return form
|
||||
|
||||
def is_logged_in(self):
|
||||
@@ -3086,16 +3091,21 @@ class Auth(object):
|
||||
if log is DEFAULT:
|
||||
log = self.messages['reset_password_log']
|
||||
userfield = self.settings.login_userfield or 'username' \
|
||||
if 'username' in table_user.fields else 'email'
|
||||
if 'username' in table_user.fields else 'email'
|
||||
if userfield=='email':
|
||||
table_user.email.requires = [
|
||||
IS_EMAIL(error_message=self.messages.invalid_email),
|
||||
IS_IN_DB(self.db, table_user.email,
|
||||
error_message=self.messages.invalid_email)]
|
||||
if not self.settings.email_case_sensitive:
|
||||
table_user.email.requires.insert(0, IS_LOWER())
|
||||
else:
|
||||
table_user.username.requires = [
|
||||
IS_IN_DB(self.db, table_user.username,
|
||||
error_message=self.messages.invalid_username)]
|
||||
if not self.settings.username_case_sensitive:
|
||||
table_user.username.requires.insert(0, IS_LOWER())
|
||||
|
||||
form = SQLFORM(table_user,
|
||||
fields=[userfield],
|
||||
hidden=dict(_next=next),
|
||||
@@ -3192,11 +3202,11 @@ class Auth(object):
|
||||
log = self.messages['change_password_log']
|
||||
passfield = self.settings.password_field
|
||||
requires = table_user[passfield].requires
|
||||
if not isinstance(requires,(list, tuple)):
|
||||
if not isinstance(requires,(list, tuple)):
|
||||
requires = [requires]
|
||||
requires = filter(lambda t:isinstance(t,CRYPT), requires)
|
||||
if requires:
|
||||
requires[0].min_length = 0
|
||||
requires[0].min_length = 0
|
||||
form = SQLFORM.factory(
|
||||
Field('old_password', 'password', requires=requires,
|
||||
label=self.messages.old_password),
|
||||
@@ -3748,7 +3758,7 @@ class Auth(object):
|
||||
archive_current=False,
|
||||
fields=None):
|
||||
"""
|
||||
If you have a table (db.mytable) that needs full revision history you
|
||||
If you have a table (db.mytable) that needs full revision history you
|
||||
can just do::
|
||||
|
||||
form=crud.update(db.mytable,myrecord,onaccept=auth.archive)
|
||||
@@ -5366,8 +5376,8 @@ class Expose(object):
|
||||
base = base or os.path.join(current.request.folder, 'static')
|
||||
basename = basename or current.request.function
|
||||
self.basename = basename
|
||||
|
||||
if current.request.raw_args:
|
||||
|
||||
if current.request.raw_args:
|
||||
self.args = [arg for arg in current.request.raw_args.split('/') if arg]
|
||||
else:
|
||||
self.args = [arg for arg in current.request.args if args]
|
||||
@@ -5691,8 +5701,8 @@ class Wiki(object):
|
||||
|
||||
def automenu(self):
|
||||
"""adds the menu if not present"""
|
||||
if (not self.wiki_menu_items and
|
||||
self.settings.controller and
|
||||
if (not self.wiki_menu_items and
|
||||
self.settings.controller and
|
||||
self.settings.function):
|
||||
self.wiki_menu_items = self.menu(self.settings.controller,
|
||||
self.settings.function)
|
||||
@@ -6164,4 +6174,3 @@ class Config(object):
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
|
||||
@@ -350,4 +350,3 @@ def getipaddrinfo(host):
|
||||
and isinstance(addrinfo[4][0], basestring)]
|
||||
except socket.error:
|
||||
return []
|
||||
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ import decimal
|
||||
import unicodedata
|
||||
from cStringIO import StringIO
|
||||
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
|
||||
from gluon.dal.objects import FieldVirtual, FieldMethod
|
||||
from pydal.objects import FieldVirtual, FieldMethod
|
||||
|
||||
regex_isint = re.compile('^[+-]?\d+$')
|
||||
|
||||
@@ -506,7 +506,7 @@ class IS_IN_DB(Validator):
|
||||
sort=False,
|
||||
_and=None,
|
||||
):
|
||||
from dal.objects import Table
|
||||
from pydal.objects import Table
|
||||
if isinstance(field, Table):
|
||||
field = field._id
|
||||
|
||||
@@ -603,7 +603,7 @@ class IS_IN_DB(Validator):
|
||||
if not [v for v in values if not v in self.theset]:
|
||||
return (values, None)
|
||||
else:
|
||||
from dal.adapters import GoogleDatastoreAdapter
|
||||
from pydal.adapters import GoogleDatastoreAdapter
|
||||
|
||||
def count(values, s=self.dbset, f=field):
|
||||
return s(f.belongs(map(int, values))).count()
|
||||
@@ -648,7 +648,7 @@ class IS_NOT_IN_DB(Validator):
|
||||
ignore_common_filters=False,
|
||||
):
|
||||
|
||||
from dal.objects import Table
|
||||
from pydal.objects import Table
|
||||
if isinstance(field, Table):
|
||||
field = field._id
|
||||
|
||||
|
||||
+1
-1
@@ -1086,7 +1086,7 @@ def start(cron=True):
|
||||
print ProgramAuthor
|
||||
print ProgramVersion
|
||||
|
||||
from dal.adapters.base import DRIVERS
|
||||
from pydal.drivers import DRIVERS
|
||||
if not options.nobanner:
|
||||
print 'Database drivers available: %s' % ', '.join(DRIVERS)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ def run(options):
|
||||
if options.ssl_certificate:
|
||||
ssl_args['certfile'] = options.ssl_certificate
|
||||
server = pywsgi.WSGIServer(
|
||||
address, application,
|
||||
address, application,
|
||||
spawn=spawn, log=None,
|
||||
**ssl_args
|
||||
)
|
||||
@@ -59,7 +59,7 @@ def main():
|
||||
default='<recycle>',
|
||||
dest='password',
|
||||
help=msg)
|
||||
|
||||
|
||||
parser.add_option('-c',
|
||||
'--ssl_certificate',
|
||||
default='',
|
||||
@@ -71,32 +71,32 @@ def main():
|
||||
default='',
|
||||
dest='ssl_private_key',
|
||||
help='file that contains ssl private key')
|
||||
|
||||
|
||||
parser.add_option('-l',
|
||||
'--logging',
|
||||
action='store_true',
|
||||
default=False,
|
||||
dest='logging',
|
||||
help='log into httpserver.log')
|
||||
|
||||
|
||||
parser.add_option('-F',
|
||||
'--profiler',
|
||||
dest='profiler_dir',
|
||||
default=None,
|
||||
help='profiler dir')
|
||||
|
||||
|
||||
parser.add_option('-i',
|
||||
'--ip',
|
||||
default='127.0.0.1',
|
||||
dest='ip',
|
||||
help='ip address')
|
||||
|
||||
|
||||
parser.add_option('-p',
|
||||
'--port',
|
||||
default='8000',
|
||||
dest='port',
|
||||
help='port number')
|
||||
|
||||
|
||||
parser.add_option('-w',
|
||||
'--workers',
|
||||
default=None,
|
||||
@@ -105,9 +105,8 @@ def main():
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
print 'starting on %s:%s...' % (
|
||||
options.ip, options.port)
|
||||
options.ip, options.port)
|
||||
run(options)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
@@ -57,8 +57,11 @@ def read_file(filename):
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
file = sys.argv[1]
|
||||
if file[-4:] == '.css':
|
||||
print cleancss(read_file(file))
|
||||
if file[-5:] == '.html':
|
||||
print cleanhtml(read_file(file))
|
||||
for file in sys.argv[1:]:
|
||||
data = read_file(file)
|
||||
open(file+'.bak2', 'w').write(data)
|
||||
if file[-4:] == '.css':
|
||||
data = cleancss(data)
|
||||
if file[-5:] == '.html':
|
||||
data = cleanhtml(data)
|
||||
open(file, 'w').write(data)
|
||||
|
||||
+10
-10
@@ -62,7 +62,7 @@ class refTable(object):
|
||||
rowSeparator = headerChar * (len(prefix) +len(postfix) + sum(maxWidths) +
|
||||
len(delim) * (len(maxWidths) - 1))
|
||||
|
||||
justify = {'center': str.center,
|
||||
justify = {'center': str.center,
|
||||
'right': str.rjust,
|
||||
'left': str.ljust
|
||||
}[justify.lower()]
|
||||
@@ -85,12 +85,12 @@ class refTable(object):
|
||||
hasHeader = False
|
||||
return output.getvalue()
|
||||
|
||||
def wrap_onspace(self, text, width):
|
||||
return reduce(lambda line, word, width=width: '%s%s%s' % (
|
||||
line,
|
||||
' ' if len(line.rsplit('\n')[-1]+word.split('\n')[0])>=width else '\n',
|
||||
def wrap_onspace(self, text, width):
|
||||
return reduce(lambda line, word, width=width: '%s%s%s' % (
|
||||
line,
|
||||
' ' if len(line.rsplit('\n')[-1]+word.split('\n')[0])>=width else '\n',
|
||||
word), text.split(' '))
|
||||
|
||||
|
||||
|
||||
def wrap_onspace_strict(self, text, width):
|
||||
wordRegex = re.compile(r'\S{' + str(width) + r',}')
|
||||
@@ -232,7 +232,7 @@ class console:
|
||||
fields.append(field.strip())
|
||||
|
||||
if len(invalidParams) > 0:
|
||||
print('the following parameter(s) is not valid\n%s' %
|
||||
print('the following parameter(s) is not valid\n%s' %
|
||||
','.join(invalidParams))
|
||||
else:
|
||||
try:
|
||||
@@ -341,10 +341,10 @@ style choices:
|
||||
print('%s has been created and populated with all available data from table %2\n' % (file, table))
|
||||
except Exception, err:
|
||||
print("EXCEPTION: could not create table %s\n%s" % (table, err))
|
||||
|
||||
|
||||
else:
|
||||
print('the following fields are not valid [%s]' % (','.join(filedNotFound)))
|
||||
|
||||
|
||||
|
||||
def cmd_help(self, *args):
|
||||
'''-3|help|Show's help'''
|
||||
@@ -477,7 +477,7 @@ class setCopyDB():
|
||||
|
||||
def delete_DB_tables(self, storageFolder, storageType):
|
||||
print 'delete_DB_tablesn\n\t%s\n\t%s' % (storageFolder, storageType)
|
||||
|
||||
|
||||
dataFiles = [storageType, "sql.log"]
|
||||
try:
|
||||
for f in os.listdir(storageFolder):
|
||||
|
||||
@@ -136,7 +136,7 @@ def define_field(conn, table, field, pks):
|
||||
else:
|
||||
f['type'] = "'blob'"
|
||||
f['comment'] = "'WARNING: Oracle Data Type %s was not mapped." % \
|
||||
str(field['DATA_TYPE']) + " Using 'blob' as fallback.'"
|
||||
str(field['DATA_TYPE']) + " Using 'blob' as fallback.'"
|
||||
|
||||
try:
|
||||
if field['COLUMN_DEFAULT']:
|
||||
|
||||
@@ -30,7 +30,7 @@ def fix_links(html,prefix):
|
||||
link = "{{=URL('static','%s/%s')}}" % (prefix,link)
|
||||
return '%s="%s"' % (href,link)
|
||||
return regex_link.sub(fix,html)
|
||||
|
||||
|
||||
def make_views(html_files,prefix):
|
||||
views = {}
|
||||
layout_name = os.path.join(prefix,'layout.html')
|
||||
@@ -76,13 +76,13 @@ def recursive_overwrite(src, dest, ignore=None):
|
||||
ignored = set()
|
||||
for f in files:
|
||||
if f not in ignored:
|
||||
recursive_overwrite(os.path.join(src, f),
|
||||
os.path.join(dest, f),
|
||||
recursive_overwrite(os.path.join(src, f),
|
||||
os.path.join(dest, f),
|
||||
ignore)
|
||||
else:
|
||||
shutil.copyfile(src, dest)
|
||||
|
||||
def convert(source, destination,prefix='imported'):
|
||||
def convert(source, destination,prefix='imported'):
|
||||
html_files = glob.glob(os.path.join(source,'*.html'))
|
||||
static_folder = os.path.join(destination,'static',prefix)
|
||||
recursive_overwrite(source,static_folder)
|
||||
@@ -96,7 +96,7 @@ def convert(source, destination,prefix='imported'):
|
||||
if not os.path.exists(os.path.split(fullname)[0]):
|
||||
os.makedirs(os.path.split(fullname)[0])
|
||||
open(fullname,'w').write(views[name])
|
||||
|
||||
|
||||
|
||||
|
||||
convert(sys.argv[1],sys.argv[2])
|
||||
|
||||
+32
-33
@@ -7,37 +7,37 @@ class LinuxService(ServiceBase):
|
||||
ServiceBase.__init__(self, name, label, stdout, stderr)
|
||||
self.pidfile = '/tmp/%s.pid' % name
|
||||
self.config_file = '/etc/%s.conf' % name
|
||||
|
||||
|
||||
def daemonize(self):
|
||||
"""
|
||||
do the UNIX double-fork magic, see Stevens' "Advanced
|
||||
do the UNIX double-fork magic, see Stevens' "Advanced
|
||||
Programming in the UNIX Environment" for details (ISBN 0201563177)
|
||||
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
|
||||
"""
|
||||
try:
|
||||
pid = os.fork()
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit first parent
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
return
|
||||
|
||||
|
||||
# decouple from parent environment
|
||||
os.chdir("/")
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
os.chdir("/")
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
# do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit from second parent
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
return
|
||||
|
||||
|
||||
# redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
@@ -47,25 +47,25 @@ class LinuxService(ServiceBase):
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
|
||||
def getpid(self):
|
||||
# Check for a pidfile to see if the daemon already runs
|
||||
try:
|
||||
try:
|
||||
pf = file(self.pidfile,'r')
|
||||
pid = int(pf.read().strip())
|
||||
pf.close()
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
|
||||
return pid
|
||||
|
||||
|
||||
def status(self):
|
||||
pid = self.getpid()
|
||||
if pid:
|
||||
return 'Service running with PID %s.' % pid
|
||||
else:
|
||||
return 'Service is not running.'
|
||||
|
||||
|
||||
def check_permissions(self):
|
||||
if not os.geteuid() == 0:
|
||||
return (False, 'This script must be run with root permissions.')
|
||||
@@ -77,12 +77,12 @@ class LinuxService(ServiceBase):
|
||||
Start the daemon
|
||||
"""
|
||||
pid = self.getpid()
|
||||
|
||||
|
||||
if pid:
|
||||
message = "Service already running under PID %s\n"
|
||||
sys.stderr.write(message % self.pidfile)
|
||||
return
|
||||
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
self.run()
|
||||
@@ -92,13 +92,13 @@ class LinuxService(ServiceBase):
|
||||
Stop the daemon
|
||||
"""
|
||||
pid = self.getpid()
|
||||
|
||||
|
||||
if not pid:
|
||||
message = "Service is not running\n"
|
||||
sys.stderr.write(message)
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
while 1:
|
||||
os.kill(pid, SIGTERM)
|
||||
@@ -118,19 +118,19 @@ class LinuxService(ServiceBase):
|
||||
"""
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
|
||||
def run(self):
|
||||
atexit.register(self.terminate)
|
||||
|
||||
|
||||
args = self.load_configuration()[0]
|
||||
stdout = open(self.stdout, 'a+')
|
||||
stderr = open(self.stderr, 'a+')
|
||||
process = subprocess.Popen(args, stdout=stdout, stderr=stderr)
|
||||
file(self.pidfile,'w+').write("%s\n" % process.pid)
|
||||
process.wait()
|
||||
|
||||
|
||||
self.terminate()
|
||||
|
||||
|
||||
def terminate(self):
|
||||
try:
|
||||
os.remove(self.pidfile)
|
||||
@@ -152,7 +152,7 @@ class LinuxService(ServiceBase):
|
||||
install_command = self.get_service_installer_command(env)
|
||||
result = self.run_command(*install_command)
|
||||
self.start()
|
||||
|
||||
|
||||
def uninstall(self):
|
||||
self.stop()
|
||||
env = self.detect_environment()
|
||||
@@ -164,7 +164,7 @@ class LinuxService(ServiceBase):
|
||||
# remove link to the script from the service directory
|
||||
path = env['rc.d-path'] + self.name
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def detect_environment(self):
|
||||
"""
|
||||
Returns a dictionary of command/path to the required command-line applications.
|
||||
@@ -196,7 +196,7 @@ class LinuxService(ServiceBase):
|
||||
env['rc.d-path'] = '/dev/null/'
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def get_service_installer_command(self, env):
|
||||
"""
|
||||
Returns list of args required to set a service to run on boot.
|
||||
@@ -218,4 +218,3 @@ class LinuxService(ServiceBase):
|
||||
else:
|
||||
cmd = env['update-rc.d']
|
||||
return [cmd, self.name, 'remove']
|
||||
|
||||
|
||||
+28
-28
@@ -17,14 +17,14 @@ class ServiceBase(Base):
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.config_file = None
|
||||
|
||||
|
||||
def load_configuration(self):
|
||||
"""
|
||||
Loads the configuration required to build the command-line string
|
||||
for running web2py. Returns a tuple (command_args, config_dict).
|
||||
"""
|
||||
s = os.path.sep
|
||||
|
||||
|
||||
default = dict(
|
||||
python = 'python',
|
||||
web2py = os.path.join(s.join(__file__.split(s)[:-3]), 'web2py.py'),
|
||||
@@ -38,14 +38,14 @@ class ServiceBase(Base):
|
||||
https_cert = '',
|
||||
password = '<recycle>',
|
||||
)
|
||||
|
||||
|
||||
config = default
|
||||
if self.config_file:
|
||||
try:
|
||||
f = open(self.config_file, 'r')
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
|
||||
|
||||
for line in lines:
|
||||
fields = line.split('=', 1)
|
||||
if len(fields) == 2:
|
||||
@@ -55,10 +55,10 @@ class ServiceBase(Base):
|
||||
config[key] = value
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
web2py_path = os.path.dirname(config['web2py'])
|
||||
os.chdir(web2py_path)
|
||||
|
||||
|
||||
args = [config['python'], config['web2py']]
|
||||
interfaces = []
|
||||
ports = []
|
||||
@@ -78,68 +78,68 @@ class ServiceBase(Base):
|
||||
ports.append(ports)
|
||||
if len(interfaces) == 0:
|
||||
sys.exit('Configuration error. Must have settings for http and/or https')
|
||||
|
||||
|
||||
password = config['password']
|
||||
if not password == '<recycle>':
|
||||
from gluon import main
|
||||
for port in ports:
|
||||
main.save_password(password, port)
|
||||
|
||||
|
||||
password = '<recycle>'
|
||||
|
||||
|
||||
args.append('-a "%s"' % password)
|
||||
|
||||
|
||||
interfaces = ';'.join(interfaces)
|
||||
args.append('--interfaces=%s' % interfaces)
|
||||
|
||||
|
||||
if 'log_filename' in config.key():
|
||||
log_filename = config['log_filename']
|
||||
args.append('--log_filename=%s' % log_filename)
|
||||
|
||||
|
||||
return (args, config)
|
||||
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
|
||||
def restart(self):
|
||||
pass
|
||||
|
||||
|
||||
def status(self):
|
||||
pass
|
||||
|
||||
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
|
||||
def install(self):
|
||||
pass
|
||||
|
||||
|
||||
def uninstall(self):
|
||||
pass
|
||||
|
||||
|
||||
def check_permissions(self):
|
||||
"""
|
||||
Does the script have permissions to install, uninstall, start, and stop services?
|
||||
Return value must be a tuple (True/False, error_message_if_False).
|
||||
"""
|
||||
return (False, 'Permissions check not implemented')
|
||||
|
||||
|
||||
class WebServerBase(Base):
|
||||
def install(self):
|
||||
pass
|
||||
|
||||
|
||||
def uninstall(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def get_service():
|
||||
service_name = 'web2py'
|
||||
service_label = 'web2py Service'
|
||||
|
||||
|
||||
if sys.platform == 'linux2':
|
||||
from linux import LinuxService as Service
|
||||
from linux import LinuxService as Service
|
||||
elif sys.platform == 'darwin':
|
||||
# from mac import MacService as Service
|
||||
sys.exit('Mac OS X is not yet supported.\n')
|
||||
@@ -148,16 +148,16 @@ def get_service():
|
||||
sys.exit('Windows is not yet supported.\n')
|
||||
else:
|
||||
sys.exit('The following platform is not supported: %s.\n' % sys.platform)
|
||||
|
||||
|
||||
service = Service(service_name, service_label)
|
||||
return service
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
service = get_service()
|
||||
is_root, error_message = service.check_permissions()
|
||||
if not is_root:
|
||||
sys.exit(error_message)
|
||||
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
command = sys.argv[1]
|
||||
if command == 'start':
|
||||
|
||||
@@ -20,12 +20,12 @@ Typical usage:
|
||||
|
||||
# Delete all sessions regardless of expiry and exit.
|
||||
python web2py.py -S app -M -R scripts/sessions2trash.py -A -o -x 0
|
||||
|
||||
|
||||
# Delete session in a module (move to the modules folder)
|
||||
from sessions2trash import single_loop
|
||||
def delete_sessions():
|
||||
single_loop()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
@@ -56,7 +56,7 @@ echo
|
||||
yum update
|
||||
|
||||
# Install required packages
|
||||
yum install httpd mod_ssl mod_wsgi wget python
|
||||
yum install httpd mod_ssl mod_wsgi wget python unzip
|
||||
|
||||
###
|
||||
### Phase 2 - Install web2py
|
||||
|
||||
Reference in New Issue
Block a user