From 3b561fb28987c37f290ad9502b8866ceb073c05b Mon Sep 17 00:00:00 2001 From: Renaud Le Gac <renaud.legac@free.fr> Date: Sat, 15 Jan 2011 17:18:34 +0000 Subject: [PATCH] Replace tab by space to follow PEP8 recommendation. --- controllers/default.py | 6 +- controllers/plugin_dbui.py | 464 ++++++------ controllers/reports.py | 69 +- models/db_database.py | 80 +- models/db_defaults.py | 56 +- models/db_languages.py | 6 +- models/db_widgets.py | 206 +++--- models/plugin_dbui.py | 30 +- modules/plugin_dbui/__init__.py | 20 +- modules/plugin_dbui/basesvc.py | 58 +- modules/plugin_dbui/cfgsvc.py | 778 ++++++++++---------- modules/plugin_dbui/dbsvc.py | 1052 +++++++++++++-------------- modules/plugin_dbui/foreignfield.py | 137 ++-- modules/plugin_dbui/setfield.py | 1 - modules/plugin_dbui/tools.py | 382 +++++----- 15 files changed, 1671 insertions(+), 1674 deletions(-) diff --git a/controllers/default.py b/controllers/default.py index f0b2d616..73676584 100644 --- a/controllers/default.py +++ b/controllers/default.py @@ -2,7 +2,7 @@ def index(): - """Main Controller to run the application. + """Main Controller to run the application. - """ - return LOAD('plugin_dbui', 'index', vars=request.vars) + """ + return LOAD('plugin_dbui', 'index', vars=request.vars) diff --git a/controllers/plugin_dbui.py b/controllers/plugin_dbui.py index d851ac1e..0fe90159 100644 --- a/controllers/plugin_dbui.py +++ b/controllers/plugin_dbui.py @@ -1,13 +1,13 @@ """ plugin_dbui.py - Controllers expose by the plugin: - - index - database - configuration + Controllers expose by the plugin: + + index + database + configuration - Author: R. Le Gac - $Id$ + Author: R. Le Gac + $Id$ """ @@ -25,234 +25,234 @@ from pprint import pprint def index(): - """ Main Controller to run the plugin - - SYNOPSIS - http://localhost:8000/application/plugin_dbui[?options] - - DESCRIPTION - Main controller to launch the plugin. - Load the extjs librairy and the associated css files. - Load the javascript librairy for the plugin. - Define view. - - PUBLIC PARAMETERS WHICH CAN BE SET VIA THE PLUGINMANAGER - - page_view - The page view call by the plugin - By default view/plugin_dbui.html - - script_path - The relative path where scripts are located. - By default static/plugin_dbui/scripts - - with_grid_filter - Add the javascript librairy for gridFilter - - PRIVATE PARAMETERS - - combined_fields - field_sets - wizards - - OPTIONS - - debug - activate the debug mode on the server side. - - script foo - run the javascript foo in the framework defined by the plugin. - The scripts are stored in the directory application/static/script - by default the javascript main.js is launch. - - EXAMPLES - - http://localhost:8000/application/plugin_dbui - http://localhost:8000/application/plugin_dbui?debug - http://localhost:8000/application/plugin_dbui?script=foo&debug - - """ - # check the presence of the extjs plugin - server_path = os.path.join('applications', request.application) - path = os.path.join(server_path, 'static', 'plugin_extjs') - if not os.path.exists(path): - raise HTTP(500, 'The plugin extjs is not install !') - - # is the plugin MatJax ? - path = os.path.join(server_path, 'static', 'plugin_mathjax') - withMathJax = (True if os.path.exists(path) else False) - - # option debug - debug = "debug" in request.get_vars - - # option script - script = (request.vars.script if "script" in request.vars else "default") - - if not script.endswith(".js"): - script = '%s.js' % script - - script_path = os.path.join(server_path, plugins.dbui.script_path) - if script not in os.listdir(script_path): - return 'Request script "%s" does not exist !!!' % script - - # css files - base = os.path.join(os.path.sep, request.application) - extjs = os.path.join(base, 'static', 'plugin_extjs') - response.files.append(os.path.join(extjs, 'resources', 'css', 'ext-all.css')) - response.files.append(os.path.join(extjs, 'examples', 'shared', 'icons', 'silk.css')) - - # javascript libraries - if debug: - response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base-debug.js')) - response.files.append(os.path.join(extjs, 'ext-all-debug.js')) - - else: - response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base.js')) - response.files.append(os.path.join(extjs, 'ext-all.js')) - - # internationalization or localization (fr, fr-fr, fr-ca, ...) - lg = T.accepted_language - if len(lg) == 5 and lg[0:2] == lg[3:5]: lg = lg[0:2] - response.files.append(os.path.join(extjs, 'src', 'locale', 'ext-lang-%s.js' % lg)) - - # plugin library -- the minified version - path = os.path.join(server_path, 'static', 'plugin_dbui', 'dbui.min.js') - if not debug and os.path.exists(path): - response.files.append(os.path.join(base, 'static', 'plugin_dbui', 'dbui.min.js')) - - # plugin libraries -- full version -- respect the alphabetic order - else: - li = [] - appjs = os.path.join(base, 'static', 'plugin_dbui', 'lib') - for file in os.listdir(os.path.join(server_path, 'static', 'plugin_dbui', 'lib')): - if file.endswith(".js"): - li.append(os.path.join(appjs, file)) - - li.sort() - response.files.extend(li) - - # main application script - response.files.append(os.path.join(base, plugins.dbui.script_path, script)) - - # page view - response.view = plugins.dbui.page_view - - return dict(debug=debug, withMathJax=withMathJax) + """ Main Controller to run the plugin + + SYNOPSIS + http://localhost:8000/application/plugin_dbui[?options] + + DESCRIPTION + Main controller to launch the plugin. + Load the extjs librairy and the associated css files. + Load the javascript librairy for the plugin. + Define view. + + PUBLIC PARAMETERS WHICH CAN BE SET VIA THE PLUGINMANAGER + + page_view + The page view call by the plugin + By default view/plugin_dbui.html + + script_path + The relative path where scripts are located. + By default static/plugin_dbui/scripts + + with_grid_filter + Add the javascript librairy for gridFilter + + PRIVATE PARAMETERS + + combined_fields + field_sets + wizards + + OPTIONS + + debug + activate the debug mode on the server side. + + script foo + run the javascript foo in the framework defined by the plugin. + The scripts are stored in the directory application/static/script + by default the javascript main.js is launch. + + EXAMPLES + + http://localhost:8000/application/plugin_dbui + http://localhost:8000/application/plugin_dbui?debug + http://localhost:8000/application/plugin_dbui?script=foo&debug + + """ + # check the presence of the extjs plugin + server_path = os.path.join('applications', request.application) + path = os.path.join(server_path, 'static', 'plugin_extjs') + if not os.path.exists(path): + raise HTTP(500, 'The plugin extjs is not install !') + + # is the plugin MatJax ? + path = os.path.join(server_path, 'static', 'plugin_mathjax') + withMathJax = (True if os.path.exists(path) else False) + + # option debug + debug = "debug" in request.get_vars + + # option script + script = (request.vars.script if "script" in request.vars else "default") + + if not script.endswith(".js"): + script = '%s.js' % script + + script_path = os.path.join(server_path, plugins.dbui.script_path) + if script not in os.listdir(script_path): + return 'Request script "%s" does not exist !!!' % script + + # css files + base = os.path.join(os.path.sep, request.application) + extjs = os.path.join(base, 'static', 'plugin_extjs') + response.files.append(os.path.join(extjs, 'resources', 'css', 'ext-all.css')) + response.files.append(os.path.join(extjs, 'examples', 'shared', 'icons', 'silk.css')) + + # javascript libraries + if debug: + response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base-debug.js')) + response.files.append(os.path.join(extjs, 'ext-all-debug.js')) + + else: + response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base.js')) + response.files.append(os.path.join(extjs, 'ext-all.js')) + + # internationalization or localization (fr, fr-fr, fr-ca, ...) + lg = T.accepted_language + if len(lg) == 5 and lg[0:2] == lg[3:5]: lg = lg[0:2] + response.files.append(os.path.join(extjs, 'src', 'locale', 'ext-lang-%s.js' % lg)) + + # plugin library -- the minified version + path = os.path.join(server_path, 'static', 'plugin_dbui', 'dbui.min.js') + if not debug and os.path.exists(path): + response.files.append(os.path.join(base, 'static', 'plugin_dbui', 'dbui.min.js')) + + # plugin libraries -- full version -- respect the alphabetic order + else: + li = [] + appjs = os.path.join(base, 'static', 'plugin_dbui', 'lib') + for file in os.listdir(os.path.join(server_path, 'static', 'plugin_dbui', 'lib')): + if file.endswith(".js"): + li.append(os.path.join(appjs, file)) + + li.sort() + response.files.extend(li) + + # main application script + response.files.append(os.path.join(base, plugins.dbui.script_path, script)) + + # page view + response.view = plugins.dbui.page_view + + return dict(debug=debug, withMathJax=withMathJax) def database(): - """ Controllers to perform operation on the database. - Act as a bridge between the web browser and the database service. - - All information defining the operation are sent via the POST/GET methods. - It contains steering keyword defining the requested action. - - Return a JSON string when the action is successful. - Otherwise return and HTTP error - - """ - debug = "debug" in request.vars and (request.vars.debug == 'true') - - if debug: - print "\nSTART DATABASE CONTROLLER" - print "Method:", request.env.request_method - print "Arguments:", request.vars - - try: - - # decode the data dictionary - if "data" in request.vars and isinstance(request.vars.data, str): - request.vars.data = json.loads(request.vars.data) - - di = dbSvc.proceed(request.vars) - resp = json.dumps(di) - - except BaseException, e: - print "Exception in the database controller:" - print "-"*72 - traceback.print_exc(file=sys.stdout) - print "-"*72 - raise HTTP(500, e) - - if debug: - print "\nDATABASE CONTROLLER RESPONSE IS:" - pprint(di) - print "END OF DATABASE CONTROLLER\n" - - return resp + """ Controllers to perform operation on the database. + Act as a bridge between the web browser and the database service. + + All information defining the operation are sent via the POST/GET methods. + It contains steering keyword defining the requested action. + + Return a JSON string when the action is successful. + Otherwise return and HTTP error + + """ + debug = "debug" in request.vars and (request.vars.debug == 'true') + + if debug: + print "\nSTART DATABASE CONTROLLER" + print "Method:", request.env.request_method + print "Arguments:", request.vars + + try: + + # decode the data dictionary + if "data" in request.vars and isinstance(request.vars.data, str): + request.vars.data = json.loads(request.vars.data) + + di = dbSvc.proceed(request.vars) + resp = json.dumps(di) + + except BaseException, e: + print "Exception in the database controller:" + print "-"*72 + traceback.print_exc(file=sys.stdout) + print "-"*72 + raise HTTP(500, e) + + if debug: + print "\nDATABASE CONTROLLER RESPONSE IS:" + pprint(di) + print "END OF DATABASE CONTROLLER\n" + + return resp def configuration(): - """ Controllers/router for the web service cfgSVc - Act as a bridge between the web browser and the configuration service. - - Relies on Ext.Direct see specification: - http://www.extjs.com/products/extjs/direct.php - - Act as a router for the cfgSvc service (see Ext.Direct Extjs 3.1.1) - Decode the raw HTTP post request: - {action: "cfgSvc", method: x, data: y, tip: i, type: "rpc"} - - Return a JSON string with the result of the request: - {action: "cfgSvc", method: x, result: y, tip: i, type: "rpc"} - - Otherwise return and HTTP error. - - For more detail on Ext.Direct see specification: - http://www.extjs.com/products/extjs/direct.php - - - """ - debug = "debug" in request.vars and (request.vars.debug == 'true') - - # NOTE: the server cfgSvc is start at the beginning - # when building the model (db.py) - - # decode the raw HTTP post - # ExtJs.3.1.1 {action: "xx", method: "y", data: [], type:"rpc", tid:2} - - req = json.loads(request.body.read()) - - if debug: - print "\nSTART CONFIGURATION CONTROLLER:" - pprint(req) - - # receive a list of dictionary for a multi-request - li = [] - if isinstance(req, list): - li.extend(req) - else: - li.append(req) - - for di in li: - # construct the command to be executed - args = di["data"] - cmd = "%s.%s(*args)" % (di["action"], di["method"]) - - if args == None: - cmd = "%s.%s()" % (di["action"], di["method"]) - - # execute the command and return HTTP exception - # when an internal error occurred - try: - res = eval(cmd) - except BaseException, e: - print "Exception in the configuration controller:" - print "-"*72 - traceback.print_exc(file=sys.stdout) - print "-"*72 - raise HTTP(500, e) - - # NOTE: the client expect the following object: - # ExtJs.3.1.1 {action: "xx", method: "y", result: [], type:"rpc", tid:2} - - del di["data"] - di["result"] = res - - if debug: - print "\nCONFIGURATION CONTROLLER RESPONSE IS:" - pprint(li) - print "\nEND OF CONFIGURATION CONTROLLER\n" - - # encode the data as a json string - return json.dumps(li) \ No newline at end of file + """ Controllers/router for the web service cfgSVc + Act as a bridge between the web browser and the configuration service. + + Relies on Ext.Direct see specification: + http://www.extjs.com/products/extjs/direct.php + + Act as a router for the cfgSvc service (see Ext.Direct Extjs 3.1.1) + Decode the raw HTTP post request: + {action: "cfgSvc", method: x, data: y, tip: i, type: "rpc"} + + Return a JSON string with the result of the request: + {action: "cfgSvc", method: x, result: y, tip: i, type: "rpc"} + + Otherwise return and HTTP error. + + For more detail on Ext.Direct see specification: + http://www.extjs.com/products/extjs/direct.php + + + """ + debug = "debug" in request.vars and (request.vars.debug == 'true') + + # NOTE: the server cfgSvc is start at the beginning + # when building the model (db.py) + + # decode the raw HTTP post + # ExtJs.3.1.1 {action: "xx", method: "y", data: [], type:"rpc", tid:2} + + req = json.loads(request.body.read()) + + if debug: + print "\nSTART CONFIGURATION CONTROLLER:" + pprint(req) + + # receive a list of dictionary for a multi-request + li = [] + if isinstance(req, list): + li.extend(req) + else: + li.append(req) + + for di in li: + # construct the command to be executed + args = di["data"] + cmd = "%s.%s(*args)" % (di["action"], di["method"]) + + if args == None: + cmd = "%s.%s()" % (di["action"], di["method"]) + + # execute the command and return HTTP exception + # when an internal error occurred + try: + res = eval(cmd) + except BaseException, e: + print "Exception in the configuration controller:" + print "-"*72 + traceback.print_exc(file=sys.stdout) + print "-"*72 + raise HTTP(500, e) + + # NOTE: the client expect the following object: + # ExtJs.3.1.1 {action: "xx", method: "y", result: [], type:"rpc", tid:2} + + del di["data"] + di["result"] = res + + if debug: + print "\nCONFIGURATION CONTROLLER RESPONSE IS:" + pprint(li) + print "\nEND OF CONFIGURATION CONTROLLER\n" + + # encode the data as a json string + return json.dumps(li) \ No newline at end of file diff --git a/controllers/reports.py b/controllers/reports.py index 0ca48497..5a6e4bd7 100644 --- a/controllers/reports.py +++ b/controllers/reports.py @@ -2,45 +2,44 @@ def index(): - """Main Controller handling report. + """Main Controller handling report. - """ - return 'Report section' + """ + return 'Report section' def report_1(): - """First trial - - """ - # Resolve foreign key - query = db.publications.id_collaborations == db.collaborations.id - query = (query) & (db.publications.id_publishers == db.publishers.id) - query = (query) & (db.publications.id_categories_aeres == db.categories_aeres.id) - - # publication with comite de lecture - query = (query) & (db.publications.conference_title == '') - query = (query) & ((db.categories_aeres.code == 'ACL') | (db.categories_aeres.code == 'ACLN')) - - # Interrogate the database - rows = db(query).select(db.publications.title, - db.publications.authors, - db.publications.id_collaborations, - db.collaborations.collaboration, - db.publishers.abbreviation, - db.publications.year, - db.publications.doi, - db.publications.volume, - db.publications.first_page, - db.publications.last_page, - db.publications.conference_title, - db.categories_aeres.code) - - - return {'title': 'Rapport 1', 'publis': rows} + """First trial + + """ + # Resolve foreign key + query = db.publications.id_collaborations == db.collaborations.id + query = (query) & (db.publications.id_publishers == db.publishers.id) + query = (query) & (db.publications.id_categories_aeres == db.categories_aeres.id) + + # publication with comite de lecture + query = (query) & (db.publications.conference_title == '') + query = (query) & ((db.categories_aeres.code == 'ACL') | (db.categories_aeres.code == 'ACLN')) + + # Interrogate the database + rows = db(query).select(db.publications.title, + db.publications.authors, + db.publications.id_collaborations, + db.collaborations.collaboration, + db.publishers.abbreviation, + db.publications.year, + db.publications.doi, + db.publications.volume, + db.publications.first_page, + db.publications.last_page, + db.publications.conference_title, + db.categories_aeres.code) + + return {'title': 'Rapport 1', 'publis': rows} def report_2(): - """ Test jsmath on a standalone page - - """ - return {} + """ Test jsmath on a standalone page + + """ + return {} diff --git a/models/db_database.py b/models/db_database.py index cdc82c69..34e848e1 100644 --- a/models/db_database.py +++ b/models/db_database.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- """ db_database - Defined the tables for your applications. - Initialized the web2py-dbui plugins - - $Id$ - + Defined the tables for your applications. + Initialized the web2py-dbui plugins + + $Id$ + """ from datetime import datetime from gluon.tools import PluginManager @@ -16,10 +16,10 @@ from gluon.tools import PluginManager # #------------------------------------------------------------------------------- plugins = PluginManager() -plugins.dbui.script_path = 'static/scripts' # js scripts for your applications +plugins.dbui.script_path = 'static/scripts' # js scripts for your applications #plugins.dbui.page_view = 'plugin_dbui.html' -T.lazy = False # immediate translation +T.lazy = False # immediate translation #------------------------------------------------------------------------------- # # connection to the database @@ -33,8 +33,8 @@ db = DAL('sqlite://storage.sqlite') # if not, use SQLite or other DB # #------------------------------------------------------------------------------- db.define_table("categories_aeres", - Field("code", "string", notnull=True), - Field("definition", "text", notnull=True)) + Field("code", "string", notnull=True), + Field("definition", "text", notnull=True)) #------------------------------------------------------------------------------- # @@ -42,7 +42,7 @@ db.define_table("categories_aeres", # #------------------------------------------------------------------------------- db.define_table("collaborations", - Field("collaboration", "string", notnull=True)) + Field("collaboration", "string", notnull=True)) #------------------------------------------------------------------------------- # @@ -50,8 +50,8 @@ db.define_table("collaborations", # #------------------------------------------------------------------------------- db.define_table("countries", - Field("country", "string", notnull=True), - Field("abbreviation", "string", notnull=True)) + Field("country", "string", notnull=True), + Field("abbreviation", "string", notnull=True)) #------------------------------------------------------------------------------- # @@ -59,7 +59,7 @@ db.define_table("countries", # #------------------------------------------------------------------------------- db.define_table("projects", - Field("project", "string", notnull=True)) + Field("project", "string", notnull=True)) #------------------------------------------------------------------------------- # @@ -67,8 +67,8 @@ db.define_table("projects", # #------------------------------------------------------------------------------- db.define_table("publishers", - Field("publisher", "string", notnull=True), - Field("abbreviation", "string", notnull=True)) + Field("publisher", "string", notnull=True), + Field("abbreviation", "string", notnull=True)) #------------------------------------------------------------------------------- # @@ -76,7 +76,7 @@ db.define_table("publishers", # #------------------------------------------------------------------------------- db.define_table("reports", - Field("type", "string", notnull=True)) + Field("type", "string", notnull=True)) #------------------------------------------------------------------------------- # @@ -84,7 +84,7 @@ db.define_table("reports", # #------------------------------------------------------------------------------- db.define_table("teams", - Field("team", "string", notnull=True)) + Field("team", "string", notnull=True)) #------------------------------------------------------------------------------- # @@ -130,29 +130,29 @@ tp_speaker = \ # the table # db.define_table("publications", - Field("title", "text", notnull=True), - Field("authors", "string", notnull=True, comment=tp_authors), - Field("id_collaborations", db.collaborations, default=1, label="Collaboration"), - Field("id_publishers", db.publishers, default=1, label="Publisher"), - Field("year", "integer", notnull=True, default=year, requires=IS_INT_IN_RANGE(1900, year+1)), - Field("doi", "string"), - Field("volume", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))), - Field("first_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))), - Field("last_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))), - Field("e_print", "string", requires=IS_EMPTY_OR(IS_URL())), - Field("conference_title", "text", label='Title'), - Field("conference_url", "string", label='url', requires=IS_EMPTY_OR(IS_URL())), - Field("conference_start", "date", label='Start date'), - Field("conference_end", "date", label='End date'), - Field("conference_town", "string", label='Town'), - Field("id_countries", db.countries, default=1, label='Country'), - Field("conference_speaker", "string", label='Speaker', comment=tp_speaker), - Field("report_numbers", "string", comment=tr_report_numbers), - Field("id_reports", db.reports, default=1, label="Report type"), - Field("authors_cppm", "text", notnull=True, comment=tp_authors_cppm), - Field("id_teams", db.teams, default=1, label='Team'), - Field("id_projects", db.projects, default=1, label='Projects'), - Field("id_categories_aeres", db.categories_aeres, default=1, label='AERES')) + Field("title", "text", notnull=True), + Field("authors", "string", notnull=True, comment=tp_authors), + Field("id_collaborations", db.collaborations, default=1, label="Collaboration"), + Field("id_publishers", db.publishers, default=1, label="Publisher"), + Field("year", "integer", notnull=True, default=year, requires=IS_INT_IN_RANGE(1900, year+1)), + Field("doi", "string"), + Field("volume", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))), + Field("first_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))), + Field("last_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))), + Field("e_print", "string", requires=IS_EMPTY_OR(IS_URL())), + Field("conference_title", "text", label='Title'), + Field("conference_url", "string", label='url', requires=IS_EMPTY_OR(IS_URL())), + Field("conference_start", "date", label='Start date'), + Field("conference_end", "date", label='End date'), + Field("conference_town", "string", label='Town'), + Field("id_countries", db.countries, default=1, label='Country'), + Field("conference_speaker", "string", label='Speaker', comment=tp_speaker), + Field("report_numbers", "string", comment=tr_report_numbers), + Field("id_reports", db.reports, default=1, label="Report type"), + Field("authors_cppm", "text", notnull=True, comment=tp_authors_cppm), + Field("id_teams", db.teams, default=1, label='Team'), + Field("id_projects", db.projects, default=1, label='Projects'), + Field("id_categories_aeres", db.categories_aeres, default=1, label='AERES')) # # define foreign keys diff --git a/models/db_defaults.py b/models/db_defaults.py index 71ff3c24..7af1bf03 100644 --- a/models/db_defaults.py +++ b/models/db_defaults.py @@ -1,53 +1,53 @@ # -*- coding: utf-8 -*- """ db_insert - Insert defaults values in the tables - - $Id$ - + Insert defaults values in the tables + + $Id$ + """ if not db(db.categories_aeres.id).count(): - db.categories_aeres.insert(id=1, code= T('undefined'), definition=T('undefined')) + db.categories_aeres.insert(id=1, code= T('undefined'), definition=T('undefined')) if not db(db.collaborations.id).count(): - db.collaborations.insert(id=1, collaboration=T('undefined')) - db.collaborations.insert(collaboration='Atlas Collaboration') - db.collaborations.insert(collaboration='H1 Collaboration') - db.collaborations.insert(collaboration='LHCb Collaboration') + db.collaborations.insert(id=1, collaboration=T('undefined')) + db.collaborations.insert(collaboration='Atlas Collaboration') + db.collaborations.insert(collaboration='H1 Collaboration') + db.collaborations.insert(collaboration='LHCb Collaboration') if not db(db.countries.id).count(): - db.countries.insert(id=1, country=T('undefined'), abbreviation=T('undefined')) - db.countries.insert(country="France", abbreviation="FR") - db.countries.insert(country="Allemagne", abbreviation="DE") - db.countries.insert(country="Etat-Unis", abbreviation="USA") + db.countries.insert(id=1, country=T('undefined'), abbreviation=T('undefined')) + db.countries.insert(country="France", abbreviation="FR") + db.countries.insert(country="Allemagne", abbreviation="DE") + db.countries.insert(country="Etat-Unis", abbreviation="USA") if not db(db.projects.id).count(): - db.projects.insert(id=1, project=T('undefined')) + db.projects.insert(id=1, project=T('undefined')) if not db(db.publishers.id).count(): - db.publishers.insert(id=1, publisher=T('undefined'), abbreviation=T('undefined')) + db.publishers.insert(id=1, publisher=T('undefined'), abbreviation=T('undefined')) - db.publishers.insert(publisher="Nuclear Instruments and Methods", - abbreviation="Nucl. Instrum. Methods Phys. Res.") - db.publishers.insert(publisher="Physics Letter B", - abbreviation="Phys. Lett. B") + db.publishers.insert(publisher="Nuclear Instruments and Methods", + abbreviation="Nucl. Instrum. Methods Phys. Res.") + db.publishers.insert(publisher="Physics Letter B", + abbreviation="Phys. Lett. B") if not db(db.reports.id).count(): - db.reports.insert(id=1, type=T('undefined')) - db.reports.insert(type="LHCb Note") + db.reports.insert(id=1, type=T('undefined')) + db.reports.insert(type="LHCb Note") if not db(db.teams.id).count(): - db.teams.insert(id=1, team=T('undefined')) - db.teams.insert(team="Atlas") - db.teams.insert(team="Antares") - db.teams.insert(team="D0") - db.teams.insert(team="H1") - db.teams.insert(team="LHCb") - db.teams.insert(team="Renoir") + db.teams.insert(id=1, team=T('undefined')) + db.teams.insert(team="Atlas") + db.teams.insert(team="Antares") + db.teams.insert(team="D0") + db.teams.insert(team="H1") + db.teams.insert(team="LHCb") + db.teams.insert(team="Renoir") diff --git a/models/db_languages.py b/models/db_languages.py index d72fb081..fb3fd361 100644 --- a/models/db_languages.py +++ b/models/db_languages.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ db_languages - Defined the languages for your applications. - $Id$ - + Defined the languages for your applications. + $Id$ + """ # Set the mother tongue of our application diff --git a/models/db_widgets.py b/models/db_widgets.py index 9f27a55d..88d1e9f6 100644 --- a/models/db_widgets.py +++ b/models/db_widgets.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ db_widgets - Customize forms and grids widgets - $Id$ - + Customize forms and grids widgets + $Id$ + """ from datetime import datetime pgm = local_import('plugin_dbui') @@ -15,13 +15,13 @@ pgm = local_import('plugin_dbui') # #------------------------------------------------------------------------------- #pgm.hidden_tables('publishers', -# 'categories_aeres', -# 'categories_commission', -## 'collaborations', -# 'countries', -## 'teams', -# 'projects', -# 'publishers') +# 'categories_aeres', +# 'categories_commission', +## 'collaborations', +# 'countries', +## 'teams', +# 'projects', +# 'publishers') #------------------------------------------------------------------------------- # @@ -29,8 +29,8 @@ pgm = local_import('plugin_dbui') # #------------------------------------------------------------------------------- pgm.customize_grid(plugins=['pGridRowEditorContextMenu', - 'pGridPaging', - 'pGridMathJax']) + 'pGridPaging', + 'pGridMathJax']) #------------------------------------------------------------------------------- @@ -45,65 +45,65 @@ pgm.customize_field('publications', 'year', maxValue=datetime.now().year) # add fieldset to form fields = ['title', - 'authors', - 'id_collaborations', - 'id_publishers', - 'year', - 'doi', - 'volume', - 'first_page', - 'last_page', - 'e_print'] + 'authors', + 'id_collaborations', + 'id_publishers', + 'year', + 'doi', + 'volume', + 'first_page', + 'last_page', + 'e_print'] pgm.customize_form_with_field_set('publications', T('General'), fields, - collapsible=True, - flex=1) + collapsible=True, + flex=1) fields = ['conference_title', - 'conference_url', - 'conference_start', - 'conference_end', - 'conference_town', - 'id_countries', - 'conference_speaker'] + 'conference_url', + 'conference_start', + 'conference_end', + 'conference_town', + 'id_countries', + 'conference_speaker'] pgm.customize_form_with_field_set('publications', T('Conference'), fields, - spacer=78, - collapsible=True, - flex=1) + spacer=78, + collapsible=True, + flex=1) fields = ['authors_cppm', - 'id_teams', - 'id_projects', - 'id_categories_aeres'] + 'id_teams', + 'id_projects', + 'id_categories_aeres'] pgm.customize_form_with_field_set('publications', 'CPPM', fields, - collapsible=True, - flex=1) + collapsible=True, + flex=1) fields = ['report_numbers', 'id_reports'] pgm.customize_form_with_field_set('publications', T('Report'), fields, - spacer=90, - collapsible=True, - flex=1) + spacer=90, + collapsible=True, + flex=1) pgm.customize_form('publications', - buttonAlign='right', - labelWidth=100, - labelAlign='right', -# layout={'type': 'hbox', 'padding': 0, 'align': 'stretchmax'}, - layout={'type': 'table', 'columns': 2}, - layoutConfig={'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}}, -# layout='table', -# layoutConfig={'columns': 2, -# 'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}}, -# default={'frame': True, 'width': 200, 'heigth': 200}, -# width=700, -# anchor='100%' - ) + buttonAlign='right', + labelWidth=100, + labelAlign='right', +# layout={'type': 'hbox', 'padding': 0, 'align': 'stretchmax'}, + layout={'type': 'table', 'columns': 2}, + layoutConfig={'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}}, +# layout='table', +# layoutConfig={'columns': 2, +# 'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}}, +# default={'frame': True, 'width': 200, 'heigth': 200}, +# width=700, +# anchor='100%' + ) #------------------------------------------------------------------------------- # @@ -113,55 +113,55 @@ pgm.customize_form('publications', # add a template column handling general information tpl = ['<b>{PublicationsTitle}</b><br>', - '{PublicationsAuthors}', - '<tpl if="PublicationsId_collaborations > 1">, {CollaborationsCollaboration}</tpl>', - '<tpl if="PublicationsDoi"><br>{PublicationsDoi}</tpl>', - '<tpl if="PublicationsId_publishers > 1"><br>{PublishersAbbreviation}', - '<tpl if="PublicationsVolume"> {PublicationsVolume}</tpl>', - '<tpl if="PublicationsYear"> ({PublicationsYear}) </tpl>', - '<tpl if="PublicationsFirst_page"> {PublicationsFirst_page}</tpl>', - '<tpl if="PublicationsLast_page">-{PublicationsLast_page}</tpl>', - '</tpl>', - '<tpl if="PublicationsE_print"><br>{PublicationsE_print}</tpl>', - '<tpl if="PublicationsConference_title"><br><br>{PublicationsConference_title}', - '<tpl if="PublicationsConference_town"><br>{PublicationsConference_town}</tpl>', - '<tpl if="CountriesCountry">, {CountriesCountry}</tpl>', - '<tpl if="PublicationsConference_start">, {PublicationsConference_start}</tpl>', - '<tpl if="PublicationsConference_end"> - {PublicationsConference_end}</tpl>', - '<tpl if="PublicationsConference_url"><br>{PublicationsConference_url}</tpl>', - '<br>%s: {PublicationsConference_speaker}' % T('Speaker'), - '</tpl>', - '<tpl if="PublicationsReport_numbers"><br><br>', - '<tpl if="PublicationsId_reports > 1">{ReportsType}, </tpl>', - '{PublicationsReport_numbers}', - '</tpl>', - '<br>Auteurs CPPM : {PublicationsAuthors_cppm}' - ] + '{PublicationsAuthors}', + '<tpl if="PublicationsId_collaborations > 1">, {CollaborationsCollaboration}</tpl>', + '<tpl if="PublicationsDoi"><br>{PublicationsDoi}</tpl>', + '<tpl if="PublicationsId_publishers > 1"><br>{PublishersAbbreviation}', + '<tpl if="PublicationsVolume"> {PublicationsVolume}</tpl>', + '<tpl if="PublicationsYear"> ({PublicationsYear}) </tpl>', + '<tpl if="PublicationsFirst_page"> {PublicationsFirst_page}</tpl>', + '<tpl if="PublicationsLast_page">-{PublicationsLast_page}</tpl>', + '</tpl>', + '<tpl if="PublicationsE_print"><br>{PublicationsE_print}</tpl>', + '<tpl if="PublicationsConference_title"><br><br>{PublicationsConference_title}', + '<tpl if="PublicationsConference_town"><br>{PublicationsConference_town}</tpl>', + '<tpl if="CountriesCountry">, {CountriesCountry}</tpl>', + '<tpl if="PublicationsConference_start">, {PublicationsConference_start}</tpl>', + '<tpl if="PublicationsConference_end"> - {PublicationsConference_end}</tpl>', + '<tpl if="PublicationsConference_url"><br>{PublicationsConference_url}</tpl>', + '<br>%s: {PublicationsConference_speaker}' % T('Speaker'), + '</tpl>', + '<tpl if="PublicationsReport_numbers"><br><br>', + '<tpl if="PublicationsId_reports > 1">{ReportsType}, </tpl>', + '{PublicationsReport_numbers}', + '</tpl>', + '<br>Auteurs CPPM : {PublicationsAuthors_cppm}' + ] hidden = ['title', - 'authors', - 'id_collaborations', - 'id_publishers', - 'doi', - 'volume', - 'first_page', - 'last_page', - 'e_print', - 'conference_title', - 'conference_url', - 'conference_start', - 'conference_end', - 'conference_town', - 'id_countries', - 'conference_speaker', - 'report_numbers', - 'id_reports', - 'authors_cppm'] + 'authors', + 'id_collaborations', + 'id_publishers', + 'doi', + 'volume', + 'first_page', + 'last_page', + 'e_print', + 'conference_title', + 'conference_url', + 'conference_start', + 'conference_end', + 'conference_town', + 'id_countries', + 'conference_speaker', + 'report_numbers', + 'id_reports', + 'authors_cppm'] pgm.customize_grid_with_template_column('publications', T('Publication'), tpl, - 1, - hidden, - width=700) + 1, + hidden, + width=700) #------------------------------------------------------------------------------- # @@ -169,10 +169,10 @@ pgm.customize_grid_with_template_column('publications', T('Publication'), tpl, # #------------------------------------------------------------------------------- filters = [('year', '==', T('select publication for a given year')), - ('id_teams', '==', T('select publications for a given team')), - ('id_projects', '==', T('select publications for a given project')), - ('id_categories_aeres', '==', T('select publications with a given AERES code')), - ('authors_cppm', 'contains', T('select publications for a given CPPM author')), - ] + ('id_teams', '==', T('select publications for a given team')), + ('id_projects', '==', T('select publications for a given project')), + ('id_categories_aeres', '==', T('select publications with a given AERES code')), + ('authors_cppm', 'contains', T('select publications for a given CPPM author')), + ] pgm.define_grid_filter('publications', filters, width=300) \ No newline at end of file diff --git a/models/plugin_dbui.py b/models/plugin_dbui.py index 158e09ca..a540a55f 100644 --- a/models/plugin_dbui.py +++ b/models/plugin_dbui.py @@ -1,4 +1,4 @@ -""" $Id$ """ +""" $Id$ """ from gluon.tools import PluginManager pgmodule = local_import('plugin_dbui') @@ -6,20 +6,20 @@ pgmodule = local_import('plugin_dbui') # define plugin parameters and their default value plugins = PluginManager('dbui', - combined_fields=None, - customized_fields={}, - customized_forms={}, - customized_grids={}, - customize_grid_with_template_column={}, - customized_viewport=None, - field_sets={}, - grid_filters={}, - hidden_columns={}, - hidden_tables=[], - page_view='plugin_dbui.html', - script_path='static/plugin_dbui/scripts', - template_columns={}, - wizards=None) + combined_fields=None, + customized_fields={}, + customized_forms={}, + customized_grids={}, + customize_grid_with_template_column={}, + customized_viewport=None, + field_sets={}, + grid_filters={}, + hidden_columns={}, + hidden_tables=[], + page_view='plugin_dbui.html', + script_path='static/plugin_dbui/scripts', + template_columns={}, + wizards=None) # Start common services diff --git a/modules/plugin_dbui/__init__.py b/modules/plugin_dbui/__init__.py index 603b36e8..7b4d04bf 100644 --- a/modules/plugin_dbui/__init__.py +++ b/modules/plugin_dbui/__init__.py @@ -1,16 +1,16 @@ """ $Id$ - Author: R. Le Gac + Author: R. Le Gac """ from cfgsvc import CfgSvc from dbsvc import DbSvc from tools import (customize_field, - customize_form, - customize_form_with_field_set, - customize_grid, - customize_grid_with_template_column, - customize_viewport, - define_combined_field, - define_grid_filter, - define_wizard, - hidden_tables) + customize_form, + customize_form_with_field_set, + customize_grid, + customize_grid_with_template_column, + customize_viewport, + define_combined_field, + define_grid_filter, + define_wizard, + hidden_tables) diff --git a/modules/plugin_dbui/basesvc.py b/modules/plugin_dbui/basesvc.py index 0aa73f5c..7b8ab753 100644 --- a/modules/plugin_dbui/basesvc.py +++ b/modules/plugin_dbui/basesvc.py @@ -1,5 +1,5 @@ """ $Id$ - Author: R. Le Gac + Author: R. Le Gac """ __version__ = "$Revision$" @@ -9,40 +9,40 @@ import pprint KEYWORD_MISSING = "The keyword '%s' is missing or undefined." class BaseSvc: - - def __init__(self, db): - self._db = db - self._vars = None + + def __init__(self, db): + self._db = db + self._vars = None - def _debug(self, *args): - """Print args if the keyword debug is in the request and - if the associate value is true. + def _debug(self, *args): + """Print args if the keyword debug is in the request and + if the associate value is true. - """ - if ("debug" in self._vars) and (self._vars.debug == 'true'): - for arg in args: - pprint.pprint(arg) + """ + if ("debug" in self._vars) and (self._vars.debug == 'true'): + for arg in args: + pprint.pprint(arg) - def _is_keyword(self, keyword): - """Raise an exception if the steering keyword is not defined.""" - if not (keyword in self._vars and self._vars[keyword]): - raise Exception, KEYWORD_MISSING % keyword + def _is_keyword(self, keyword): + """Raise an exception if the steering keyword is not defined.""" + if not (keyword in self._vars and self._vars[keyword]): + raise Exception, KEYWORD_MISSING % keyword - - def _list(self, val): - """ If val is a list return val. - Otherwise, build a list containing val - - """ - if not isinstance(val, (list, tuple)): - val = [val] - return val + + def _list(self, val): + """ If val is a list return val. + Otherwise, build a list containing val + + """ + if not isinstance(val, (list, tuple)): + val = [val] + return val - def proceed(self, vars): - """Proceed the request sends via the POST/GET method. + def proceed(self, vars): + """Proceed the request sends via the POST/GET method. - """ - self._vars = vars + """ + self._vars = vars diff --git a/modules/plugin_dbui/cfgsvc.py b/modules/plugin_dbui/cfgsvc.py index 4f22d32e..aec2d96b 100644 --- a/modules/plugin_dbui/cfgsvc.py +++ b/modules/plugin_dbui/cfgsvc.py @@ -1,5 +1,5 @@ """ $Id$ - Author: R. Le Gac + Author: R. Le Gac """ __version__ = "$Revision$" @@ -15,395 +15,395 @@ FIELD_IN_DBFIELD = "Field %s already in the dbFields list." # Convert a field type into a widget xtype # The dictionary contain the configuration parameters of the Ext JS widget FTYPE_TO_XTYPE = {'boolean': {'xtype': 'checkbox'},\ - 'date': {'xtype': 'datefield', 'format': 'Y-m-d'},\ - 'datetime': {'xtype': 'datefield', 'format': 'Y-m-d H:i:s'},\ - 'double': {'xtype': 'numberfield'},\ - 'id': {'xtype': 'textfield'},\ - 'integer': {'xtype': 'numberfield'},\ - 'password': {'xtype': 'textfield', 'inputType': 'password'},\ - 'reference': {'xtype': 'xcombobox'},\ - 'string': {'xtype': 'textfield'},\ - 'text': {'xtype': 'textarea'},\ - 'time': {'xtype': 'timefield'},\ - 'upload': {'xtype': 'textfield', 'inputType': 'file'}} + 'date': {'xtype': 'datefield', 'format': 'Y-m-d'},\ + 'datetime': {'xtype': 'datefield', 'format': 'Y-m-d H:i:s'},\ + 'double': {'xtype': 'numberfield'},\ + 'id': {'xtype': 'textfield'},\ + 'integer': {'xtype': 'numberfield'},\ + 'password': {'xtype': 'textfield', 'inputType': 'password'},\ + 'reference': {'xtype': 'xcombobox'},\ + 'string': {'xtype': 'textfield'},\ + 'text': {'xtype': 'textarea'},\ + 'time': {'xtype': 'timefield'},\ + 'upload': {'xtype': 'textfield', 'inputType': 'file'}} class CfgSvc(object): - - def __init__(self, db, translator, pluginManager): - - self._db = db - self._foreign = ForeignField(db) - self._set = SetField(db) - self._translate = translator - - for key in pluginManager.dbui: - setattr(self, '_%s' % key, pluginManager.dbui[key]) - - - def _getColumn(self, tablename, fieldname): - """ Return a dictionary with the column model - required by the gridPanel widget. - - """ - - col = {} - field = self._db[tablename][fieldname] - - # column header - col["header"] = str(self._translate(field.label)) - - # replace foreign key by the pointing column - if self._is_foreign_field(tablename, fieldname): - - k_tablename, k_fieldname, k_key = \ - self._get_foreign_data(tablename, fieldname) - col["dataIndex"] = encode_field(k_tablename, k_fieldname) - - # standard column - else: - col["dataIndex"] = encode_field(tablename, fieldname) - - col["sortable"] = True - - # hide the primary key - # and field which are not readable and not writable - if fieldname == "id" or ((not field.readable) and (not field.writable)): - col["hidden"] = True - - # Hide fields request customizing the grid - if tablename in self._hidden_columns: - if fieldname in self._hidden_columns[tablename]: - col["hidden"] = True - - return col - - - def _getColumnsModel(self, tablename): - """ Return the columns model required by the gridPanel widget - handling customize columns. - - """ - - li= [] - - # standard column - for fieldname in self._db[tablename].fields: - li.append(self._getColumn(tablename, fieldname)) - - # template column - if tablename in self._template_columns: - for el in self._template_columns[tablename]: - - col = {'xtype': 'templatecolumn', - 'header': el.header, - 'tpl': el.tpl, - 'sortable': True} - - col.update(el.extjs) - - li.insert(el.ipos, col) - - return li - - - def _get_foreign_data(self, tablename, fieldname): - """ Wrapper method returning the foreign data. - the foreign data is a tuple containing the name of the - pointing table, the name of the pointing field - and the join instruction. - - """ - return self._foreign.get_foreign_data(tablename, fieldname) - - - def _get_set_data(self, tablename, fieldname): - """ Wrapper method - - """ - return self._set.get_set_data(tablename, fieldname) - - - def _getFormField(self, tablename, fieldname): - """ Return the configuration dictionary for a form field. - - """ - field = self._db[tablename][fieldname] - cfg= {"name": encode_field(tablename, fieldname)} - - # foreign key - if self._is_foreign_field(tablename, fieldname): - - k_tablename, k_fieldname, k_key = \ - self._get_foreign_data(tablename, fieldname) - - cfg["xtype"] = "xcombobox" - cfg["hiddenName"] = encode_field(tablename, fieldname) - cfg["displayField"] = encode_field(k_tablename, k_fieldname) - cfg["valueField"] = encode_field(k_tablename, k_key) - - dbfields = [[k_tablename, k_fieldname], [k_tablename, k_key]] - fields = [encode_field(k_tablename, k_fieldname), - encode_field(k_tablename, k_key)] - - cfg["model"] = {"store": {"table": k_tablename, - "dbFields": dbfields, - "fields": fields}} - - # set field - elif self._is_set_field(tablename, fieldname): - cfg["xtype"] = "xsetbox" - cfg["model"] = {"setData": self._get_set_data(tablename, fieldname)} - - # other types of fields - else: - cfg.update(FTYPE_TO_XTYPE[field.type]) - - # Exploit the argument of the SQLField consructor - # field label - cfg["fieldLabel"] = str(self._translate(field.label)) - - # default value and not null - # NOTE: the entryform doesn't work when both default and - # notnull condition are defined. - if field.default: - cfg["value"] = str(field.default) - elif field.notnull: - cfg["allowBlank"] = False - - # read only field - if field.readable and not field.writable: - cfg["readOnly"] = True - - # tool tip - if field.comment: - cfg["tipText"] = field.comment - - # hide primary key - # and field which are not readable and not writable - if field.name == "id" or ((not field.readable) and (not field.writable)): - cfg["hidden"] = True - cfg["hideLabel"] = True - cfg["readOnly"] = True - - # customized field - if tablename in self._customized_fields and \ - fieldname in self._customized_fields[tablename]: - cfg.update(self._customized_fields[tablename][fieldname]) - - return cfg - - - def _getFormModelItems(self, tablename): - """ Return the list of items appearing in a Form model. - - """ - li, id_is_not_used = [], True - - # form with field sets - # NOTE: the field id is add in order to run this form - # in all context, create, update, destroy. - # It is add to the latest fieldset. - - if tablename in self._field_sets: - for fieldset in self._field_sets[tablename]: - - cfg = {'title': fieldset.title, - 'xtype': 'fieldset', - 'items': []} - - for fieldname in fieldset.fields: - cfg['items'].append(self._getFormField(tablename, fieldname)) - if fieldname == 'id': - id_is_not_used = False - - # protection when applying Ext JS configuration parameters - # NOTE: if the user submit a list of items they are append at the - # end of the current list. - # It is a way to add spacer and to fine tune the layout - di = dict(fieldset.extjs) - if 'items' in di: - cfg['items'].extend(di['items']) - del di['items'] - - # Apply the remaining Ext JS configuration parameters - cfg.update(di) - li.append(cfg) - - if id_is_not_used: - li[-1]['items'].append(self._getFormField(tablename, 'id')) - - # the default form - else: - for fieldname in self._db[tablename].fields: - li.append(self._getFormField(tablename, fieldname)) - - return li - - - def _getGridFilter(self, tablename): - """ Generate configuration object for the grid filter - - """ - di = {} - - if tablename in self._grid_filters: - - di = {'xtype':'xgridfilter', - 'items': [], - 'title': self._translate('Filter %s' % tablename)} - - filters = self._grid_filters[tablename].filters - for (fieldname, operator, comment) in filters: - - # get the standard form field - cfg = self._getFormField(tablename, fieldname) - - # remove default value for fields which are not combobox - if cfg['xtype'] != 'xcombobox': - cfg['value'] = '' - cfg['allowBlank'] = True - - # replace all textarea by textfield - if cfg['xtype'] == 'textarea': - cfg['xtype'] = 'textfield' - - # prepare the name for the where close - cfg['name'] = '[%s.%s]' % (tablename, fieldname) - - # store information to customize the filter - cfg['filterComment'] = comment - cfg['filterOperator'] = operator - cfg['filterType'] = self._db[tablename][fieldname].type - - di['items'].append(cfg) - - extjs = self._grid_filters[tablename].extjs - di.update(extjs) - - return di - - - def _getJsonStore(self, table): - """ Generate configuration object for JSON stores - required by the gridPanel widget. - - """ - - di = {"table": table, - "idProperty": encode_field(table,'id'), - "dbFields": [], - "fields": []} - - for field in self._db[table].fields: - if field in di["dbFields"]: - raise Exception, FIELD_IN_DBFIELD - - di["dbFields"].append([table, field]) - di["fields"].append(encode_field(table, field)) - - if self._is_foreign_field(table, field): - if "where" not in di: di["where"] = [] - k_table, k_field, k_key = self._get_foreign_data(table, field) - - if k_field in di["dbFields"]: - raise Exception, FIELD_IN_DBFIELD - - di["dbFields"].append([k_table, k_field]) - di["fields"].append(encode_field(k_table, k_field)) - di["where"].append("[%s.%s] == [%s.%s]" % (table, field, k_table, k_key)) - - return di - - - def _getMethods(self): - """ Return the list of methods available and - their number of arguments. - - """ - li = [] - for el in dir(self): - if el.startswith("_"): continue - di = {"name": el,\ - "len": eval("self.%s.func_code.co_argcount" % el)-1} - li.append(di) - - return li - - - def _is_foreign_field(self, tablename, fieldname): - """ Wrapper method - - """ - return self._foreign.is_foreign_field(tablename, fieldname) - - - def _is_set_field(self, tablename, fieldname): - """ Wrapper method - - """ - return self._set.is_set_field(tablename, fieldname) - - - def getFormModel(self, tablename): - """ Return the configuration dictionary for an entryForm widget. - - """ - model = {"title": str(self._translate(tablename.title())), - "table": tablename, - "items": self._getFormModelItems(tablename)} - - di = {"xtype": "xentry", "model": model} - - for el in (ALL, tablename): - if el in self._customized_forms: - di.update(self._customized_forms[el]) - - return di - - - def getGridModel(self, tablename): - """ Return the configuration dictionary for a gridPanel widget. - - """ - - formModel = {'xtype': 'xform', - 'items': self._getFormModelItems(tablename)} - - for el in (ALL, tablename): - if tablename in self._customized_forms: - formModel.update(self._customized_forms[tablename]) - - model = {} - model["colModel"] = self._getColumnsModel(tablename) - model["formModel"] = formModel - - filter = self._getGridFilter(tablename) - if filter: - model["filterModel"] = filter - - model["store"] = self._getJsonStore(tablename) - model["table"] = tablename - model["title"] = str(self._translate(tablename.title())) - - di = {"xtype": "xgrid", "model": model} - - # handle the user customization - for el in (ALL, tablename): - if el in self._customized_grids: - di.update(self._customized_grids[el]) - - return di - - - def getTables(self, *args): - """ Return a dictionary with list of tables - and a list with the translation of their names - - """ - li_1 = [el for el in self._db.tables if el not in self._hidden_tables] - li_1.sort() - li_2 = [str(self._translate(el)) for el in li_1] - - return {'tables': li_1, 'tablesText': li_2} \ No newline at end of file + + def __init__(self, db, translator, pluginManager): + + self._db = db + self._foreign = ForeignField(db) + self._set = SetField(db) + self._translate = translator + + for key in pluginManager.dbui: + setattr(self, '_%s' % key, pluginManager.dbui[key]) + + + def _getColumn(self, tablename, fieldname): + """ Return a dictionary with the column model + required by the gridPanel widget. + + """ + + col = {} + field = self._db[tablename][fieldname] + + # column header + col["header"] = str(self._translate(field.label)) + + # replace foreign key by the pointing column + if self._is_foreign_field(tablename, fieldname): + + k_tablename, k_fieldname, k_key = \ + self._get_foreign_data(tablename, fieldname) + col["dataIndex"] = encode_field(k_tablename, k_fieldname) + + # standard column + else: + col["dataIndex"] = encode_field(tablename, fieldname) + + col["sortable"] = True + + # hide the primary key + # and field which are not readable and not writable + if fieldname == "id" or ((not field.readable) and (not field.writable)): + col["hidden"] = True + + # Hide fields request customizing the grid + if tablename in self._hidden_columns: + if fieldname in self._hidden_columns[tablename]: + col["hidden"] = True + + return col + + + def _getColumnsModel(self, tablename): + """ Return the columns model required by the gridPanel widget + handling customize columns. + + """ + + li= [] + + # standard column + for fieldname in self._db[tablename].fields: + li.append(self._getColumn(tablename, fieldname)) + + # template column + if tablename in self._template_columns: + for el in self._template_columns[tablename]: + + col = {'xtype': 'templatecolumn', + 'header': el.header, + 'tpl': el.tpl, + 'sortable': True} + + col.update(el.extjs) + + li.insert(el.ipos, col) + + return li + + + def _get_foreign_data(self, tablename, fieldname): + """ Wrapper method returning the foreign data. + the foreign data is a tuple containing the name of the + pointing table, the name of the pointing field + and the join instruction. + + """ + return self._foreign.get_foreign_data(tablename, fieldname) + + + def _get_set_data(self, tablename, fieldname): + """ Wrapper method + + """ + return self._set.get_set_data(tablename, fieldname) + + + def _getFormField(self, tablename, fieldname): + """ Return the configuration dictionary for a form field. + + """ + field = self._db[tablename][fieldname] + cfg= {"name": encode_field(tablename, fieldname)} + + # foreign key + if self._is_foreign_field(tablename, fieldname): + + k_tablename, k_fieldname, k_key = \ + self._get_foreign_data(tablename, fieldname) + + cfg["xtype"] = "xcombobox" + cfg["hiddenName"] = encode_field(tablename, fieldname) + cfg["displayField"] = encode_field(k_tablename, k_fieldname) + cfg["valueField"] = encode_field(k_tablename, k_key) + + dbfields = [[k_tablename, k_fieldname], [k_tablename, k_key]] + fields = [encode_field(k_tablename, k_fieldname), + encode_field(k_tablename, k_key)] + + cfg["model"] = {"store": {"table": k_tablename, + "dbFields": dbfields, + "fields": fields}} + + # set field + elif self._is_set_field(tablename, fieldname): + cfg["xtype"] = "xsetbox" + cfg["model"] = {"setData": self._get_set_data(tablename, fieldname)} + + # other types of fields + else: + cfg.update(FTYPE_TO_XTYPE[field.type]) + + # Exploit the argument of the SQLField consructor + # field label + cfg["fieldLabel"] = str(self._translate(field.label)) + + # default value and not null + # NOTE: the entryform doesn't work when both default and + # notnull condition are defined. + if field.default: + cfg["value"] = str(field.default) + elif field.notnull: + cfg["allowBlank"] = False + + # read only field + if field.readable and not field.writable: + cfg["readOnly"] = True + + # tool tip + if field.comment: + cfg["tipText"] = field.comment + + # hide primary key + # and field which are not readable and not writable + if field.name == "id" or ((not field.readable) and (not field.writable)): + cfg["hidden"] = True + cfg["hideLabel"] = True + cfg["readOnly"] = True + + # customized field + if tablename in self._customized_fields and \ + fieldname in self._customized_fields[tablename]: + cfg.update(self._customized_fields[tablename][fieldname]) + + return cfg + + + def _getFormModelItems(self, tablename): + """ Return the list of items appearing in a Form model. + + """ + li, id_is_not_used = [], True + + # form with field sets + # NOTE: the field id is add in order to run this form + # in all context, create, update, destroy. + # It is add to the latest fieldset. + + if tablename in self._field_sets: + for fieldset in self._field_sets[tablename]: + + cfg = {'title': fieldset.title, + 'xtype': 'fieldset', + 'items': []} + + for fieldname in fieldset.fields: + cfg['items'].append(self._getFormField(tablename, fieldname)) + if fieldname == 'id': + id_is_not_used = False + + # protection when applying Ext JS configuration parameters + # NOTE: if the user submit a list of items they are append at the + # end of the current list. + # It is a way to add spacer and to fine tune the layout + di = dict(fieldset.extjs) + if 'items' in di: + cfg['items'].extend(di['items']) + del di['items'] + + # Apply the remaining Ext JS configuration parameters + cfg.update(di) + li.append(cfg) + + if id_is_not_used: + li[-1]['items'].append(self._getFormField(tablename, 'id')) + + # the default form + else: + for fieldname in self._db[tablename].fields: + li.append(self._getFormField(tablename, fieldname)) + + return li + + + def _getGridFilter(self, tablename): + """ Generate configuration object for the grid filter + + """ + di = {} + + if tablename in self._grid_filters: + + di = {'xtype':'xgridfilter', + 'items': [], + 'title': self._translate('Filter %s' % tablename)} + + filters = self._grid_filters[tablename].filters + for (fieldname, operator, comment) in filters: + + # get the standard form field + cfg = self._getFormField(tablename, fieldname) + + # remove default value for fields which are not combobox + if cfg['xtype'] != 'xcombobox': + cfg['value'] = '' + cfg['allowBlank'] = True + + # replace all textarea by textfield + if cfg['xtype'] == 'textarea': + cfg['xtype'] = 'textfield' + + # prepare the name for the where close + cfg['name'] = '[%s.%s]' % (tablename, fieldname) + + # store information to customize the filter + cfg['filterComment'] = comment + cfg['filterOperator'] = operator + cfg['filterType'] = self._db[tablename][fieldname].type + + di['items'].append(cfg) + + extjs = self._grid_filters[tablename].extjs + di.update(extjs) + + return di + + + def _getJsonStore(self, table): + """ Generate configuration object for JSON stores + required by the gridPanel widget. + + """ + + di = {"table": table, + "idProperty": encode_field(table,'id'), + "dbFields": [], + "fields": []} + + for field in self._db[table].fields: + if field in di["dbFields"]: + raise Exception, FIELD_IN_DBFIELD + + di["dbFields"].append([table, field]) + di["fields"].append(encode_field(table, field)) + + if self._is_foreign_field(table, field): + if "where" not in di: di["where"] = [] + k_table, k_field, k_key = self._get_foreign_data(table, field) + + if k_field in di["dbFields"]: + raise Exception, FIELD_IN_DBFIELD + + di["dbFields"].append([k_table, k_field]) + di["fields"].append(encode_field(k_table, k_field)) + di["where"].append("[%s.%s] == [%s.%s]" % (table, field, k_table, k_key)) + + return di + + + def _getMethods(self): + """ Return the list of methods available and + their number of arguments. + + """ + li = [] + for el in dir(self): + if el.startswith("_"): continue + di = {"name": el,\ + "len": eval("self.%s.func_code.co_argcount" % el)-1} + li.append(di) + + return li + + + def _is_foreign_field(self, tablename, fieldname): + """ Wrapper method + + """ + return self._foreign.is_foreign_field(tablename, fieldname) + + + def _is_set_field(self, tablename, fieldname): + """ Wrapper method + + """ + return self._set.is_set_field(tablename, fieldname) + + + def getFormModel(self, tablename): + """ Return the configuration dictionary for an entryForm widget. + + """ + model = {"title": str(self._translate(tablename.title())), + "table": tablename, + "items": self._getFormModelItems(tablename)} + + di = {"xtype": "xentry", "model": model} + + for el in (ALL, tablename): + if el in self._customized_forms: + di.update(self._customized_forms[el]) + + return di + + + def getGridModel(self, tablename): + """ Return the configuration dictionary for a gridPanel widget. + + """ + + formModel = {'xtype': 'xform', + 'items': self._getFormModelItems(tablename)} + + for el in (ALL, tablename): + if tablename in self._customized_forms: + formModel.update(self._customized_forms[tablename]) + + model = {} + model["colModel"] = self._getColumnsModel(tablename) + model["formModel"] = formModel + + filter = self._getGridFilter(tablename) + if filter: + model["filterModel"] = filter + + model["store"] = self._getJsonStore(tablename) + model["table"] = tablename + model["title"] = str(self._translate(tablename.title())) + + di = {"xtype": "xgrid", "model": model} + + # handle the user customization + for el in (ALL, tablename): + if el in self._customized_grids: + di.update(self._customized_grids[el]) + + return di + + + def getTables(self, *args): + """ Return a dictionary with list of tables + and a list with the translation of their names + + """ + li_1 = [el for el in self._db.tables if el not in self._hidden_tables] + li_1.sort() + li_2 = [str(self._translate(el)) for el in li_1] + + return {'tables': li_1, 'tablesText': li_2} \ No newline at end of file diff --git a/modules/plugin_dbui/dbsvc.py b/modules/plugin_dbui/dbsvc.py index 46f22fc6..19b188c0 100644 --- a/modules/plugin_dbui/dbsvc.py +++ b/modules/plugin_dbui/dbsvc.py @@ -1,5 +1,5 @@ """ $Id$ - Author: R. Le Gac + Author: R. Le Gac """ __version__ = "$Revision$" @@ -15,16 +15,16 @@ ACTIONS = ('create', 'destroy', 'read', 'update') # see gluon.dal.Field FUNC_FIELDS = ('count', - 'day', - 'lower', - 'max', - 'min', - 'minutes', - 'month', - 'upper', - 'seconds', - 'sum', - 'year') + 'day', + 'lower', + 'max', + 'min', + 'minutes', + 'month', + 'upper', + 'seconds', + 'sum', + 'year') ACTION_INVALID = "Requested action '%s' is not valid." FIELD_NOT_IN_DB = "the field '%s' doesn't exist in the database." @@ -38,518 +38,518 @@ WHERE_INVALID = "Invalid syntax for a where keywords." class DbSvc(BaseSvc): - """ DbSvc - - Interface the database and database model with http request. - It is the glue between wep2py and javascript. - - Handle HTTP POST and HTTP GET request. - The selection of the action and its parameters are based on - steering keywords: xaction, table, dbFields, debug,... - The xaction is mandatory. - - Main actions are: - - create - create a new record in a table. - - read - read the content of a single table - and handle query with foreign key and where conditions - - select - read the content of several table handling complex - query with join, group by, order by keywords. - - update - update the record of a table. - - destroy - delete the record of a table. - - The steering keyword table is mandatory for the actions - create, read, update and destroy. - - the steering keyword for the select operation is xaction=read - but without table definition. - - Each action return a dictionary: - {success: True, data:{blabla}, msg: 'blalbla'} - - The keyword success is always present. It is mandatory for - the FormPanel widget of the extjs library, but not strictly - required since failure can be propagated through HTTP errors. - - In the data dictionary database field are encode as TableField. - - If the action failed an Exception is raised. - The exception can be catch to generate an HTTP error. - - The most important method are DbSvc.proceed(), DbSvc.create(), - DbSvc.destroy(), DbSvc.read(), DbSvc.select(), DbSvc.update() - - """ - - KEYWORDS = ['data', - 'debug', - 'dbFields', - 'dir', - 'groupby', - 'leftjoin', - 'limit', - 'orderby', - 'sort', - 'start', - 'table', - 'where', - 'xaction'] - - - def _encode_query(self, li): - """ helper method to encode the list of query as a web2py query. - - "[table1, field1] == [table2, field2]" - "[table1, field1] > n" - "[table1, field1] like 'm%'" - "[table1, field1] contains 'm%'" - "[table1, field1] startswith 'm%'" - "[table1, field1].lower() like 'm%'" - "[table1, field1] belongs (1, 2, 3)" - "([table1, field1] == [table2, field2]) | ([table1, field2] == [table2, field1])" - - all element of the list are ANDED - operator are ==, !=, <, >, <=, >= - boolean operator are &, | - """ - query = "" - for el in li: - - s = el.replace(' ','') - s = s.replace(',', '.') - s = s.replace('[', 'db.') - s = s.replace(']', '') - - for op in ['belongs', 'contains', 'like', 'startswith']: - if op in s: - s = "%s)" % s.replace(op, '.%s(' % op ) - - query = ("(%s) & (%s)" % (query, s) if query else s) - - return eval(query, {}, {"db": self._db}) - - - def _get_fields(self): - """Helper method returning a dictionary with request fields - and values. - - The App.data.JsonStore send the field and their values - as steering keywords for FormPanel. - - Since EXtJs 3.1, the fields and their values are - encapsulated in the data dictionary for a GridPanel. - - This method allows to extract field values in all cases. - - """ - # New approach coming with ExtJs 3.1 where fields values are - # encapsulated in the data dictionary - if 'data' in self._vars: - return self._vars['data'] - - # preserve backward compatibility for formPanel - fields = dict(self._vars) - for key in self.KEYWORDS: - if key in fields: - del fields[key] - return fields - - - def _is_action_valid(self, action): - """Raise an Exception if the request action is not handle.""" - if action not in ACTIONS: - raise Exception, ACTION_INVALID % action - - - def _is_field_in_table(self, table, field): - """ Return true is the field belongs to the table. """ - return field in self._db[table].fields - - - def _is_fields_values_valid(self, table, fields): - """Return a dictionary {field1: error1, field2: error2,...} - - The value of each field is check against validators defined in the - data model. The return dictionary contains field failing these checks. - - the dictionary is empty when everything is OK. - - """ - di = {} - for (field, val) in fields.items(): - errmsg = self._db[table][field].validate(val)[1] - if val and self._db[table][field].notnull and errmsg: - di[field] = errmsg - return di - - - def _is_table_in_db(self, table): - """Raise an Exception if the table is not defined in the database.""" - if table not in self._db.tables: - raise Exception, TABLE_NOT_IN_DB % table - - - def create(self): - """Create a new record in a table. - - debug - boolean - - table - the table name where data will be inserted. - - data - a dictionary with field values {field1: x, field2: y,...} - approach use by App.data.JsonStore embedded in GridPanel - since ExtJs 3.1 - - field1, field2,... - To keep backward compatibility with formPanel - the field value. - - - Return a dictionary with status, error message and id of the insert row: - {success: False, errors: {field: 'blabla',..}} - The keyword success and error are required by - the Ext.FormPanel widget. - - or - - {success: True, msg: 'blalbla', data:{id:xx}} - - """ - self._debug("Start DbSvc.insert") - - self._is_keyword("table") - - table = self._vars.table - self._is_table_in_db(table) - - # Remove fields which do not belong to the table. - # The JsonStore send the foreign key and the the pointing field - # when manipulating table with foreign keys. The latter belongs - # to another table. - - fields, di = {}, self._get_fields() - for table_field in di: - tablename, fieldname = decode_field(table_field) - if tablename == table: - fields[fieldname.encode('utf8')] = di[table_field] - - # Validate field contents - di = self._is_fields_values_valid(table, fields) - if di: - return {"success": False, "errors": di} - - # Create the new record - self._debug('fields -->', fields) - id = self._db[table].insert (**fields) - - self._debug("End DbSvc.insert.") - resp = {"success": True, - "msg": RECORD_INSERTED % id, - "data": {encode_field(table,'id'): id}} - return resp - - - def destroy(self): - """Destroy a record in a table. - - debug - boolean - - table - the table name where record will be removed. - - data - dictionary with the identifier of the record (id) - - Return a dictionary with status and error message: - {success: True, msg: 'blalbla'} - - """ - self._debug("Start DbSvc.destroy") - - self._is_keyword("table") - self._is_keyword("data") - - table = self._vars.table - self._is_table_in_db(table) - - id = self._vars.data[encode_field(table, "id")] - del self._db[table][id] - - self._debug("End DbSvc.destroy") - return {"success": True, "msg": RECORD_DELETED % id} - - - def proceed(self, vars): - """Proceed the request sends via the POST/GET method. - - the input vars is a Storage object. - - Return a dictionary with status, data and error message: - {success: True, data:{blabla}, msg: 'blalbla'} - - """ - BaseSvc.proceed(self, vars) - self._debug("Start DbSvc.proceed") - - self._is_keyword("xaction") - self._is_action_valid(vars.xaction) - - # create, destroy, update and read action related to a single table - # they are used to manipulate record within a table - if "table" in vars: - self._debug("self.%s()" % vars.xaction) - di = eval("self.%s()" % vars.xaction) - - # complex query involving several tables and conditions - # they are use to build view or report - elif vars.xaction == 'read': - di = self.select() - - else: - raise Exception, ACTION_INVALID % "" - - self._debug("End DbSvc.proceed") - return di - - - def read(self): - """Read the content of a single table resolving foreign keys. - - debug - A boolean - - table - name of the table containing records - - dbFields - A list of field to be red. - It also include pointing field resolving foreign keys. - [(table1, field1), (table1, field2), (table2, field3), ...] - - where (optional) - define the inner join when handling foreign key. - The syntax follows the web2py convention: - ['db.table1.field1 == db.table2.id']. - An AND is performed between the different - elements of the list. - - - This method also understand parameters useful for paging. - For more detail see Ext.PagingToolbar. - start - limit - sort - dir - - Return a dictionary with selected data: - {success: True, data: [{field1: xxx,..},...]} - - """ - self._debug("Start DbSvc.read") - self._is_keyword("dbFields") - - self._is_keyword("table") - table = self._vars.table - self._is_table_in_db(table) - - # build the list of SQLField object - # The JSON process sends a string when only one field is defined - # NOTE: The json encoding return the tuple [table, field] - # as a string 'table, field' - fields = [] - for el in self._list(self._vars.dbFields): - (table, field) = el.replace(' ','').split(',') - if not self._is_field_in_table(table, field): - raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field)) - fields.append(self._db[table][field]) - - # build the inner join conditions - # The JSON process sends a string when only one field is defined - query = '' - if "where" in self._vars: - query = self._encode_query(self._list(self._vars["where"])) - - # handle paging options (see Ext.PagingToolbar) - # Count the number of record in the table --require for the paging - kwargs, results = {}, None - if 'start' in self._vars and 'limit' in self._vars: - start = int(self._vars.start) - limit = int(self._vars.limit) - kwargs['limitby'] = (start, start+limit) - results = self._db(self._db[table].id).count() - - rows = self._db(query).select(*fields, **kwargs) - - self._debug("End DbSvc.read") - return {"success": True, "results": results, "data": rows_serializer(rows)} - - - def select(self): - """ Handle complex queries with inner/left join and with where - group by, order by conditions. - - debug - A boolean - - dbFields - A list of field to be red. - [(table1, field1), (table2, field1), ....] - - where (optional) - define the inner join when handling foreign key. - The syntax follows the web2py convention: - ['db.table1.field1 == db.table2.id', 'db.table2.field3 > 3',...]. - An AND is performed between the different - elements of the list. - - left (optional) - - groupby (optional) - - orderby (optional) - - distinct (optional) - - - This method also understand parameters useful for paging. - For more detail see Ext.PagingToolbar. - start - limit - sort - dir - - Return a dictionary with selected data: - {success: True, data: [{Table1Field1: xxx,..},...]} - - """ - self._debug("Start DbSvc.read") - self._is_keyword("dbFields") - - # build the list of SQLField object - # The JSON process sends a string when only one field is defined - # but a list in the other case. - # NOTE: The json encoding return the tuple [table, field, func] - # as a string 'table, field, func' - fields = [] - for el in self._list(self._vars.dbFields): - li = el.replace(' ','').split(',') - (table, field) = li[:2] - if not self._is_field_in_table(table, field): - raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field)) - - if len(li) == 2: - fields.append(self._db[table][field]) - - elif len(li) == 3: - func = li[2] - if func not in FUNC_FIELDS: - raise Exception, FUNC_FIELD_INVALID % func - - dbfield = "db.%s.%s.%s()" % (table, field, func) - fields.append(eval(dbfield, {}, {"db": self._db})) - - # build the innerJoins / where conditions - # The JSON process sends a string when only one field is defined - query = '' - if "where" in self._vars: - query = self._encode_query(self._list(self._vars["where"])) - - # handle left join - kwargs = {} - if "table" in self._vars and "left" in self._vars: - table = self._vars.table - self._is_table_in_db(table) - left_query = self._encode_query(self._list(self._vars["left"])) - kwargs["left"] = self._db[table].on(left_query) - - # handle orderby - if "orderby" in self._vars: - for el in self._list(self._vars.orderby): - (table, field) = el.replace(' ','').split(',') - if "orderby" in kwargs: - kwargs["orderby"] = kwargs["orderby"] | self._db[table][field] - else: - kwargs["orderby"] = self._db[table][field] - - # handle group by - if "groupby" in self._vars: - (table, field) = self._vars.groupby.replace(' ','').split(',') - kwargs["groupby"] = self._db[table][field] - - # handle distinct - if "distinct" in self._vars: - kwargs["distinct"] = True - - # handle paging options (see Ext.PagingToolbar) - if 'start' in self._vars and 'limit' in self._vars: - kwargs['limitby'] = (int(self._vars.start), int(self._vars.limit)) - - # proceed the select - rows = self._db(query).select(*fields, **kwargs) - - self._debug("End DbSvc.read") - return {"success": True, "data": rows_serializer(rows)} - - - def update(self): - """Update a record in a table. - - The steering keywords are: - - debug - boolean - - table - the table name where data will be updated - - data - A dictionary containing the update values for field - as well as the identifier of the record to be updated (id) - - - Return a dictionary with status, message and id of the update row: - {success: True, msg: 'blalbla', data:{id:xx}} - - """ - self._debug("Start DbSvc.update.") - - self._is_keyword("table") - self._is_keyword("data") - - table = self._vars.table - self._is_table_in_db(table) - - # Remove fields which do not belong to the table. - # The JsonStore send the foreign key and the the pointing field - # when manipulating table with foreign keys. the latter belongs - # to another table. - - fields = {} - for table_field in self._vars.data: - tablename, fieldname = decode_field(table_field) - if tablename == table: - fields[fieldname.encode('utf8')] = self._vars.data[table_field] - - # Validate field contents - di = self._is_fields_values_valid(table, fields) - if di: - return {"success": False, "errors": di} - - id = fields["id"] - self._db[table][id] = fields - - self._debug("End DbSvc.update.") - return {"success": True, "msg": RECORD_UPDATED % id, "data": {"id": id}} + """ DbSvc + + Interface the database and database model with http request. + It is the glue between wep2py and javascript. + + Handle HTTP POST and HTTP GET request. + The selection of the action and its parameters are based on + steering keywords: xaction, table, dbFields, debug,... + The xaction is mandatory. + + Main actions are: + + create + create a new record in a table. + + read + read the content of a single table + and handle query with foreign key and where conditions + + select + read the content of several table handling complex + query with join, group by, order by keywords. + + update + update the record of a table. + + destroy + delete the record of a table. + + The steering keyword table is mandatory for the actions + create, read, update and destroy. + + the steering keyword for the select operation is xaction=read + but without table definition. + + Each action return a dictionary: + {success: True, data:{blabla}, msg: 'blalbla'} + + The keyword success is always present. It is mandatory for + the FormPanel widget of the extjs library, but not strictly + required since failure can be propagated through HTTP errors. + + In the data dictionary database field are encode as TableField. + + If the action failed an Exception is raised. + The exception can be catch to generate an HTTP error. + + The most important method are DbSvc.proceed(), DbSvc.create(), + DbSvc.destroy(), DbSvc.read(), DbSvc.select(), DbSvc.update() + + """ + + KEYWORDS = ['data', + 'debug', + 'dbFields', + 'dir', + 'groupby', + 'leftjoin', + 'limit', + 'orderby', + 'sort', + 'start', + 'table', + 'where', + 'xaction'] + + + def _encode_query(self, li): + """ helper method to encode the list of query as a web2py query. + + "[table1, field1] == [table2, field2]" + "[table1, field1] > n" + "[table1, field1] like 'm%'" + "[table1, field1] contains 'm%'" + "[table1, field1] startswith 'm%'" + "[table1, field1].lower() like 'm%'" + "[table1, field1] belongs (1, 2, 3)" + "([table1, field1] == [table2, field2]) | ([table1, field2] == [table2, field1])" + + all element of the list are ANDED + operator are ==, !=, <, >, <=, >= + boolean operator are &, | + """ + query = "" + for el in li: + + s = el.replace(' ','') + s = s.replace(',', '.') + s = s.replace('[', 'db.') + s = s.replace(']', '') + + for op in ['belongs', 'contains', 'like', 'startswith']: + if op in s: + s = "%s)" % s.replace(op, '.%s(' % op ) + + query = ("(%s) & (%s)" % (query, s) if query else s) + + return eval(query, {}, {"db": self._db}) + + + def _get_fields(self): + """Helper method returning a dictionary with request fields + and values. + + The App.data.JsonStore send the field and their values + as steering keywords for FormPanel. + + Since EXtJs 3.1, the fields and their values are + encapsulated in the data dictionary for a GridPanel. + + This method allows to extract field values in all cases. + + """ + # New approach coming with ExtJs 3.1 where fields values are + # encapsulated in the data dictionary + if 'data' in self._vars: + return self._vars['data'] + + # preserve backward compatibility for formPanel + fields = dict(self._vars) + for key in self.KEYWORDS: + if key in fields: + del fields[key] + return fields + + + def _is_action_valid(self, action): + """Raise an Exception if the request action is not handle.""" + if action not in ACTIONS: + raise Exception, ACTION_INVALID % action + + + def _is_field_in_table(self, table, field): + """ Return true is the field belongs to the table. """ + return field in self._db[table].fields + + + def _is_fields_values_valid(self, table, fields): + """Return a dictionary {field1: error1, field2: error2,...} + + The value of each field is check against validators defined in the + data model. The return dictionary contains field failing these checks. + + the dictionary is empty when everything is OK. + + """ + di = {} + for (field, val) in fields.items(): + errmsg = self._db[table][field].validate(val)[1] + if val and self._db[table][field].notnull and errmsg: + di[field] = errmsg + return di + + + def _is_table_in_db(self, table): + """Raise an Exception if the table is not defined in the database.""" + if table not in self._db.tables: + raise Exception, TABLE_NOT_IN_DB % table + + + def create(self): + """Create a new record in a table. + + debug + boolean + + table + the table name where data will be inserted. + + data + a dictionary with field values {field1: x, field2: y,...} + approach use by App.data.JsonStore embedded in GridPanel + since ExtJs 3.1 + + field1, field2,... + To keep backward compatibility with formPanel + the field value. + + + Return a dictionary with status, error message and id of the insert row: + {success: False, errors: {field: 'blabla',..}} + The keyword success and error are required by + the Ext.FormPanel widget. + + or + + {success: True, msg: 'blalbla', data:{id:xx}} + + """ + self._debug("Start DbSvc.insert") + + self._is_keyword("table") + + table = self._vars.table + self._is_table_in_db(table) + + # Remove fields which do not belong to the table. + # The JsonStore send the foreign key and the the pointing field + # when manipulating table with foreign keys. The latter belongs + # to another table. + + fields, di = {}, self._get_fields() + for table_field in di: + tablename, fieldname = decode_field(table_field) + if tablename == table: + fields[fieldname.encode('utf8')] = di[table_field] + + # Validate field contents + di = self._is_fields_values_valid(table, fields) + if di: + return {"success": False, "errors": di} + + # Create the new record + self._debug('fields -->', fields) + id = self._db[table].insert (**fields) + + self._debug("End DbSvc.insert.") + resp = {"success": True, + "msg": RECORD_INSERTED % id, + "data": {encode_field(table,'id'): id}} + return resp + + + def destroy(self): + """Destroy a record in a table. + + debug + boolean + + table + the table name where record will be removed. + + data + dictionary with the identifier of the record (id) + + Return a dictionary with status and error message: + {success: True, msg: 'blalbla'} + + """ + self._debug("Start DbSvc.destroy") + + self._is_keyword("table") + self._is_keyword("data") + + table = self._vars.table + self._is_table_in_db(table) + + id = self._vars.data[encode_field(table, "id")] + del self._db[table][id] + + self._debug("End DbSvc.destroy") + return {"success": True, "msg": RECORD_DELETED % id} + + + def proceed(self, vars): + """Proceed the request sends via the POST/GET method. + + the input vars is a Storage object. + + Return a dictionary with status, data and error message: + {success: True, data:{blabla}, msg: 'blalbla'} + + """ + BaseSvc.proceed(self, vars) + self._debug("Start DbSvc.proceed") + + self._is_keyword("xaction") + self._is_action_valid(vars.xaction) + + # create, destroy, update and read action related to a single table + # they are used to manipulate record within a table + if "table" in vars: + self._debug("self.%s()" % vars.xaction) + di = eval("self.%s()" % vars.xaction) + + # complex query involving several tables and conditions + # they are use to build view or report + elif vars.xaction == 'read': + di = self.select() + + else: + raise Exception, ACTION_INVALID % "" + + self._debug("End DbSvc.proceed") + return di + + + def read(self): + """Read the content of a single table resolving foreign keys. + + debug + A boolean + + table + name of the table containing records + + dbFields + A list of field to be red. + It also include pointing field resolving foreign keys. + [(table1, field1), (table1, field2), (table2, field3), ...] + + where (optional) + define the inner join when handling foreign key. + The syntax follows the web2py convention: + ['db.table1.field1 == db.table2.id']. + An AND is performed between the different + elements of the list. + + + This method also understand parameters useful for paging. + For more detail see Ext.PagingToolbar. + start + limit + sort + dir + + Return a dictionary with selected data: + {success: True, data: [{field1: xxx,..},...]} + + """ + self._debug("Start DbSvc.read") + self._is_keyword("dbFields") + + self._is_keyword("table") + table = self._vars.table + self._is_table_in_db(table) + + # build the list of SQLField object + # The JSON process sends a string when only one field is defined + # NOTE: The json encoding return the tuple [table, field] + # as a string 'table, field' + fields = [] + for el in self._list(self._vars.dbFields): + (table, field) = el.replace(' ','').split(',') + if not self._is_field_in_table(table, field): + raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field)) + fields.append(self._db[table][field]) + + # build the inner join conditions + # The JSON process sends a string when only one field is defined + query = '' + if "where" in self._vars: + query = self._encode_query(self._list(self._vars["where"])) + + # handle paging options (see Ext.PagingToolbar) + # Count the number of record in the table --require for the paging + kwargs, results = {}, None + if 'start' in self._vars and 'limit' in self._vars: + start = int(self._vars.start) + limit = int(self._vars.limit) + kwargs['limitby'] = (start, start+limit) + results = self._db(self._db[table].id).count() + + rows = self._db(query).select(*fields, **kwargs) + + self._debug("End DbSvc.read") + return {"success": True, "results": results, "data": rows_serializer(rows)} + + + def select(self): + """ Handle complex queries with inner/left join and with where + group by, order by conditions. + + debug + A boolean + + dbFields + A list of field to be red. + [(table1, field1), (table2, field1), ....] + + where (optional) + define the inner join when handling foreign key. + The syntax follows the web2py convention: + ['db.table1.field1 == db.table2.id', 'db.table2.field3 > 3',...]. + An AND is performed between the different + elements of the list. + + left (optional) + + groupby (optional) + + orderby (optional) + + distinct (optional) + + + This method also understand parameters useful for paging. + For more detail see Ext.PagingToolbar. + start + limit + sort + dir + + Return a dictionary with selected data: + {success: True, data: [{Table1Field1: xxx,..},...]} + + """ + self._debug("Start DbSvc.read") + self._is_keyword("dbFields") + + # build the list of SQLField object + # The JSON process sends a string when only one field is defined + # but a list in the other case. + # NOTE: The json encoding return the tuple [table, field, func] + # as a string 'table, field, func' + fields = [] + for el in self._list(self._vars.dbFields): + li = el.replace(' ','').split(',') + (table, field) = li[:2] + if not self._is_field_in_table(table, field): + raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field)) + + if len(li) == 2: + fields.append(self._db[table][field]) + + elif len(li) == 3: + func = li[2] + if func not in FUNC_FIELDS: + raise Exception, FUNC_FIELD_INVALID % func + + dbfield = "db.%s.%s.%s()" % (table, field, func) + fields.append(eval(dbfield, {}, {"db": self._db})) + + # build the innerJoins / where conditions + # The JSON process sends a string when only one field is defined + query = '' + if "where" in self._vars: + query = self._encode_query(self._list(self._vars["where"])) + + # handle left join + kwargs = {} + if "table" in self._vars and "left" in self._vars: + table = self._vars.table + self._is_table_in_db(table) + left_query = self._encode_query(self._list(self._vars["left"])) + kwargs["left"] = self._db[table].on(left_query) + + # handle orderby + if "orderby" in self._vars: + for el in self._list(self._vars.orderby): + (table, field) = el.replace(' ','').split(',') + if "orderby" in kwargs: + kwargs["orderby"] = kwargs["orderby"] | self._db[table][field] + else: + kwargs["orderby"] = self._db[table][field] + + # handle group by + if "groupby" in self._vars: + (table, field) = self._vars.groupby.replace(' ','').split(',') + kwargs["groupby"] = self._db[table][field] + + # handle distinct + if "distinct" in self._vars: + kwargs["distinct"] = True + + # handle paging options (see Ext.PagingToolbar) + if 'start' in self._vars and 'limit' in self._vars: + kwargs['limitby'] = (int(self._vars.start), int(self._vars.limit)) + + # proceed the select + rows = self._db(query).select(*fields, **kwargs) + + self._debug("End DbSvc.read") + return {"success": True, "data": rows_serializer(rows)} + + + def update(self): + """Update a record in a table. + + The steering keywords are: + + debug + boolean + + table + the table name where data will be updated + + data + A dictionary containing the update values for field + as well as the identifier of the record to be updated (id) + + + Return a dictionary with status, message and id of the update row: + {success: True, msg: 'blalbla', data:{id:xx}} + + """ + self._debug("Start DbSvc.update.") + + self._is_keyword("table") + self._is_keyword("data") + + table = self._vars.table + self._is_table_in_db(table) + + # Remove fields which do not belong to the table. + # The JsonStore send the foreign key and the the pointing field + # when manipulating table with foreign keys. the latter belongs + # to another table. + + fields = {} + for table_field in self._vars.data: + tablename, fieldname = decode_field(table_field) + if tablename == table: + fields[fieldname.encode('utf8')] = self._vars.data[table_field] + + # Validate field contents + di = self._is_fields_values_valid(table, fields) + if di: + return {"success": False, "errors": di} + + id = fields["id"] + self._db[table][id] = fields + + self._debug("End DbSvc.update.") + return {"success": True, "msg": RECORD_UPDATED % id, "data": {"id": id}} diff --git a/modules/plugin_dbui/foreignfield.py b/modules/plugin_dbui/foreignfield.py index c09fb45b..58d9b72e 100644 --- a/modules/plugin_dbui/foreignfield.py +++ b/modules/plugin_dbui/foreignfield.py @@ -1,5 +1,5 @@ """ $Id$ - Author: R. Le Gac + Author: R. Le Gac """ __version__ = "$Revision$" @@ -12,72 +12,71 @@ K_FIELD = "k_field" K_KEY = "k_key" class ForeignField(dict): - - def __init__(self, db): - """ Scan the database model to identify foreign key, - Store the pointing table and field. - - """ - - for table in db.tables: - fields = [db[table][el] for el in db[table].fields] - - for field in fields: - # - # FOREIGN FIELD - # - # The reference to the foreign table is defined in the - # type: "reference foreignTable". - # The foreign table and the associated column are also - # defined in the validator IS_IN_DB. - # - if field.type.startswith("reference"): - - validators = field.requires - if not isinstance(validators, (list, tuple)): - validators = [field.requires] - - for vdt in validators: - if isinstance(vdt, IS_IN_DB): - - if table not in self: - self[table] = {} - - self[table][field.name] = {} - self[table][field.name][K_TABLE] = vdt.ktable - self[table][field.name][K_FIELD] = vdt.ks[0] - self[table][field.name][K_KEY] = vdt.ks[1] - - - def is_foreign_field(self, table, field): - """ Return true is the table.field is a foreign key. - - """ - if table in self: - return field in self[table] - - else: - return False - - - def is_table_with_foreign_fields(self, table): - return (True if table in self else False) - - - def get_foreign_data(self, table, field): - """ Return a tuple containing the pointing table, the pointing field - and key for the join: - - table.field --> k_table.k_field - table.field == k_table.k_key - - Otherwise return None. - - """ - if self.is_foreign_field(table, field): - di = self[table][field] - return (di[K_TABLE], di[K_FIELD], di[K_KEY]) - - else: - return None + + def __init__(self, db): + """ Scan the database model to identify foreign key, + Store the pointing table and field. + + """ + for table in db.tables: + fields = [db[table][el] for el in db[table].fields] + + for field in fields: + # + # FOREIGN FIELD + # + # The reference to the foreign table is defined in the + # type: "reference foreignTable". + # The foreign table and the associated column are also + # defined in the validator IS_IN_DB. + # + if field.type.startswith("reference"): + + validators = field.requires + if not isinstance(validators, (list, tuple)): + validators = [field.requires] + + for vdt in validators: + if isinstance(vdt, IS_IN_DB): + + if table not in self: + self[table] = {} + + self[table][field.name] = {} + self[table][field.name][K_TABLE] = vdt.ktable + self[table][field.name][K_FIELD] = vdt.ks[0] + self[table][field.name][K_KEY] = vdt.ks[1] + + + def is_foreign_field(self, table, field): + """ Return true is the table.field is a foreign key. + + """ + if table in self: + return field in self[table] + + else: + return False + + + def is_table_with_foreign_fields(self, table): + return (True if table in self else False) + + + def get_foreign_data(self, table, field): + """ Return a tuple containing the pointing table, the pointing field + and key for the join: + + table.field --> k_table.k_field + table.field == k_table.k_key + + Otherwise return None. + + """ + if self.is_foreign_field(table, field): + di = self[table][field] + return (di[K_TABLE], di[K_FIELD], di[K_KEY]) + + else: + return None diff --git a/modules/plugin_dbui/setfield.py b/modules/plugin_dbui/setfield.py index 23b39607..53aa3417 100644 --- a/modules/plugin_dbui/setfield.py +++ b/modules/plugin_dbui/setfield.py @@ -61,4 +61,3 @@ class SetField(dict): else: return None - diff --git a/modules/plugin_dbui/tools.py b/modules/plugin_dbui/tools.py index 54d05350..e436d159 100644 --- a/modules/plugin_dbui/tools.py +++ b/modules/plugin_dbui/tools.py @@ -1,5 +1,5 @@ """ $Id$ - Author: R. Le Gac + Author: R. Le Gac """ __version__ = "$Revision$" @@ -12,222 +12,222 @@ ALL = '_all' def _init_tool(param, type='dict'): - """ Return the plugin manager with the parameter param - initialize as a dictionary, if it does not exist. - - If can be a list if the type is equal to list. - - """ - p = PluginManager('dbui') - if param not in p.dbui: - if type == 'dict': - p.dbui[param] = {} - elif type == 'list': - p.dbui[param] = [] - return p + """ Return the plugin manager with the parameter param + initialize as a dictionary, if it does not exist. + + If can be a list if the type is equal to list. + + """ + p = PluginManager('dbui') + if param not in p.dbui: + if type == 'dict': + p.dbui[param] = {} + elif type == 'list': + p.dbui[param] = [] + return p def customize_field(tablename, fieldname, **extjs): - """ Helper tool to customize an existing field by applying the - Ext JS configuration parameters. - - NOTE: If the xtype is defined as a parameter the default widget - will be replaced by the new one -- not recommand. - - """ - p = _init_tool('customized_fields') - if tablename not in p.dbui.customized_fields: - p.dbui.customized_fields[tablename] = {} - p.dbui.customized_fields[tablename][fieldname] = extjs + """ Helper tool to customize an existing field by applying the + Ext JS configuration parameters. + + NOTE: If the xtype is defined as a parameter the default widget + will be replaced by the new one -- not recommand. + + """ + p = _init_tool('customized_fields') + if tablename not in p.dbui.customized_fields: + p.dbui.customized_fields[tablename] = {} + p.dbui.customized_fields[tablename][fieldname] = extjs def customize_form(tablename=None, **extjs): - """ Helper tool to apply Ext JS configuration parameters on existing form. - For details see the documentation of Ext.form.formPanel. - - The most interesting parameters are labelwidth, labelAlign, - labelSeparator, defaults, layout, layoutConfig and width. - - If tablename is not define, the parameters will be apply to - all forms. The order is the following apply first the table - parameters then the general ones. - - """ - p = _init_tool('customized_forms') - key = tablename - if key == None: key = ALL - p.dbui.customized_forms[key] = extjs - + """ Helper tool to apply Ext JS configuration parameters on existing form. + For details see the documentation of Ext.form.formPanel. + + The most interesting parameters are labelwidth, labelAlign, + labelSeparator, defaults, layout, layoutConfig and width. + + If tablename is not define, the parameters will be apply to + all forms. The order is the following apply first the table + parameters then the general ones. + + """ + p = _init_tool('customized_forms') + key = tablename + if key == None: key = ALL + p.dbui.customized_forms[key] = extjs + def customize_form_with_field_set(tablename, title, fields, spacer=0, **extjs): - """ Helper tool to customize an existing form with fieldset. - - By default fields will occupy all the space in the fieldset panel. - this behavior can be superseded using the Ext JS 'defaults' configuration - parameter. - - - tablename - name of the tablename - - title - title of the fieldset - - fields - list of field name, belonging to the tablename, - groups in the fieldset. - - spacer - add a Ext.Spacer at the end of the item list. - The value defines the height of the spacer. - Useful to tune the final height of the field set. - - extjs - additional keyword arguments corresponding - to Ext.form.FieldSet configuration parameters - (See the Ext JS APIdocumentation) - - """ - p = _init_tool('field_sets') - if tablename not in p.dbui.field_sets: - p.dbui.field_sets[tablename] = [] - - # by default all fields occupy the full space in the fieldset panel - if 'defaults' in extjs: - if 'anchor' not in extjs['defaults']: - extjs['defaults'] = '100%' - else: - extjs['defaults'] = {'anchor': '100%'} - - # add the spacer component - if spacer: - di = {'xtype': 'spacer', 'height': spacer} - if 'items' in extjs: - items.append(di) - else: - extjs['items'] = [di] - - di = Storage(title=title, fields=fields, extjs= extjs) - p.dbui.field_sets[tablename].append(di) + """ Helper tool to customize an existing form with fieldset. + + By default fields will occupy all the space in the fieldset panel. + this behavior can be superseded using the Ext JS 'defaults' configuration + parameter. + + + tablename + name of the tablename + + title + title of the fieldset + + fields + list of field name, belonging to the tablename, + groups in the fieldset. + + spacer + add a Ext.Spacer at the end of the item list. + The value defines the height of the spacer. + Useful to tune the final height of the field set. + + extjs + additional keyword arguments corresponding + to Ext.form.FieldSet configuration parameters + (See the Ext JS APIdocumentation) + + """ + p = _init_tool('field_sets') + if tablename not in p.dbui.field_sets: + p.dbui.field_sets[tablename] = [] + + # by default all fields occupy the full space in the fieldset panel + if 'defaults' in extjs: + if 'anchor' not in extjs['defaults']: + extjs['defaults'] = '100%' + else: + extjs['defaults'] = {'anchor': '100%'} + + # add the spacer component + if spacer: + di = {'xtype': 'spacer', 'height': spacer} + if 'items' in extjs: + items.append(di) + else: + extjs['items'] = [di] + + di = Storage(title=title, fields=fields, extjs= extjs) + p.dbui.field_sets[tablename].append(di) def customize_grid(tablename=None, **extjs): - """ Helper tool to apply Ext JS configuration parameters on existing grid. - For details see the documentation of Ext.grid.GridPanel. + """ Helper tool to apply Ext JS configuration parameters on existing grid. + For details see the documentation of Ext.grid.GridPanel. - The most interesting parameter is plugins: - pGridRowEditorContextMenu, pGridPaging, .... + The most interesting parameter is plugins: + pGridRowEditorContextMenu, pGridPaging, .... - If tablename is not define, the parameters will be apply to - all grids. The order is the following apply first the table - parameters then the general ones. - - """ - p = _init_tool('customized_grids') - key = tablename - if key == None: key = ALL - p.dbui.customized_grids[key] = extjs + If tablename is not define, the parameters will be apply to + all grids. The order is the following apply first the table + parameters then the general ones. + + """ + p = _init_tool('customized_grids') + key = tablename + if key == None: key = ALL + p.dbui.customized_grids[key] = extjs def customize_grid_with_template_column(tablename, - header, - tpl, - ipos=0, - hidden=[], - **extjs): - """ Helper tool to add a template column to an existing grid. - - tablename - name of the table associate to the grid - - header - Header of the column in the grid - - tpl - Ext.Template for the column - - ipos - position of the column in the grid - i.e 0 mean the first column, -1 inserts - before the last element - - hidden - List of columns to be hidden in the grid since they - are aggregate in the column template. Columns are identified - via their fieldname. - - extjs - additional keyword arguments corresponding - to Ext.grid.Column configuration parameters - (See the documentation of Ext.grid.Column) - - """ - p = _init_tool('template_columns') - p = _init_tool('hidden_columns') - - if tablename not in p.dbui.template_columns: - p.dbui.template_columns[tablename] = [] - p.dbui.hidden_columns[tablename] = [] - - di = Storage(header=header, tpl=tpl, ipos=ipos, extjs=extjs) - p.dbui.template_columns[tablename].append(di) - - for el in hidden: - if el not in p.dbui.hidden_columns[tablename]: - p.dbui.hidden_columns[tablename].append(el) - - + header, + tpl, + ipos=0, + hidden=[], + **extjs): + """ Helper tool to add a template column to an existing grid. + + tablename + name of the table associate to the grid + + header + Header of the column in the grid + + tpl + Ext.Template for the column + + ipos + position of the column in the grid + i.e 0 mean the first column, -1 inserts + before the last element + + hidden + List of columns to be hidden in the grid since they + are aggregate in the column template. Columns are identified + via their fieldname. + + extjs + additional keyword arguments corresponding + to Ext.grid.Column configuration parameters + (See the documentation of Ext.grid.Column) + + """ + p = _init_tool('template_columns') + p = _init_tool('hidden_columns') + + if tablename not in p.dbui.template_columns: + p.dbui.template_columns[tablename] = [] + p.dbui.hidden_columns[tablename] = [] + + di = Storage(header=header, tpl=tpl, ipos=ipos, extjs=extjs) + p.dbui.template_columns[tablename].append(di) + + for el in hidden: + if el not in p.dbui.hidden_columns[tablename]: + p.dbui.hidden_columns[tablename].append(el) + + def customize_viewport(): - """ Helper tool to customize the viewport - - """ - p = _init_tool('customized_viewport') + """ Helper tool to customize the viewport + + """ + p = _init_tool('customized_viewport') def define_combined_field(): - """ Helper tool to define combined fields in form. - - """ - p = _init_tool('combined_fields') + """ Helper tool to define combined fields in form. + + """ + p = _init_tool('combined_fields') def define_grid_filter(tablename, filters, **extjs): - """ Helper tool to define filter associate to grid. - - tablename - name of the database table - - filters - a list of tuple. - Each tuple contains three columns: - -- a string with the database field - -- a string with the operator - the list of valid operator is defined in - the method dbsvc._encode_query - -- a string with a comment for the tool tip - extjs - additional keyword arguments corresponding - to the configuration parameter of Ext.form.FieldSet - (see the Ext JS API documentation) - """ - p = _init_tool('grid_filters') - - di = Storage(filters=filters, extjs=extjs) - p.dbui.grid_filters[tablename] = di + """ Helper tool to define filter associate to grid. + + tablename + name of the database table + + filters + a list of tuple. + Each tuple contains three columns: + -- a string with the database field + -- a string with the operator + the list of valid operator is defined in + the method dbsvc._encode_query + -- a string with a comment for the tool tip + extjs + additional keyword arguments corresponding + to the configuration parameter of Ext.form.FieldSet + (see the Ext JS API documentation) + """ + p = _init_tool('grid_filters') + + di = Storage(filters=filters, extjs=extjs) + p.dbui.grid_filters[tablename] = di def define_wizard(): - """ Helper tool to define wizard in form. - - """ - p = _init_tool('wizards') + """ Helper tool to define wizard in form. + + """ + p = _init_tool('wizards') def hidden_tables(*args): - """ Helper tools to define a set of tables to be hidden in the viewport. - - """ - p = _init_tool('hidden_tables', 'list') - p.dbui.hidden_tables.extend(args) - - \ No newline at end of file + """ Helper tools to define a set of tables to be hidden in the viewport. + + """ + p = _init_tool('hidden_tables', 'list') + p.dbui.hidden_tables.extend(args) + + \ No newline at end of file -- GitLab