Commit 222a08d3 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Merge branch '14-pylint-jslint' into 'master'

14 pylint jslint

* Code is compliant with pylint
* Code is compliant with jslint
* Add the `jshint strict` construct since `jshint` is used in Liclipse.
* Close #14

See merge request !10
parents 0d424751 8445b2db
......@@ -29,4 +29,4 @@ def country_update(s, f):
"""
countries = current.globalenv['db']["countries"]
countries[CALLBACK_ERRORS] = "Cant't update a Country."
return True
\ No newline at end of file
return True
......@@ -78,7 +78,6 @@ from helper import (as_list,
get_language,
get_plugin_path,
get_reference_paths,
get_set_field,
get_script_path,
get_set_field,
get_store_id,
......
""" base class to build service
"""
import pprint
......@@ -15,7 +14,6 @@ class BaseSvc:
def __init__(self):
self.debug = False
def dbg(self, *args):
"""Print the arguments when the attribute debug is true.
......
......@@ -7,39 +7,39 @@ from dbsvc import CALLBACK_ERRORS
from gluon import current
def INHIBIT_DELETE_UNDEF(set):
def INHIBIT_DELETE_UNDEF(setrows):
"""Inhibit the delete of the record containing the undefined value.
Args:
set (gluon.dal.Set):
setrows (gluon.dal.Set):
set of records used for deleted
Returns:
true when the record contains the undefined value.
"""
db, T = current.globalenv['db'], current.T
field = set.query.first
T = current.T
field = setrows.query.first
# protection
# the query of the set should be "table.id == 1"
if field._db._adapter.EQ != set.query.op:
if field._db._adapter.EQ != setrows.query.op:
return False
# inhibit the delete
if field.name == 'id' and set.query.second == UNDEF_ID:
if field.name == 'id' and setrows.query.second == UNDEF_ID:
field._table[CALLBACK_ERRORS] = \
T("You can not delete the row containing the undefined value.")
T("You can not delete the row containing the undefined value.")
return True
return False
def INHIBIT_UPDATE_UNDEF(set, values):
def INHIBIT_UPDATE_UNDEF(setrows, values):
"""Inhibit the update the row containing the undefined value.
Args:
set (gluon.dal.Set):
setrows (gluon.dal.Set):
set of records for update
values (dict):
......@@ -49,18 +49,18 @@ def INHIBIT_UPDATE_UNDEF(set, values):
true when the record contains the undefined value.
"""
db, T = current.globalenv['db'], current.T
field = set.query.first
T = current.T
field = setrows.query.first
# protection
# the query of the set should be "table.id == 1"
if set.query.op != field._db._adapter.EQ:
if setrows.query.op != field._db._adapter.EQ:
return False
# inhibit the delete
if field.name == 'id' and int(set.query.second) == UNDEF_ID:
if field.name == 'id' and int(setrows.query.second) == UNDEF_ID:
field._table[CALLBACK_ERRORS] = \
T("You can not update the row containing the undefined value.")
T("You can not update the row containing the undefined value.")
return True
return False
......@@ -4,8 +4,32 @@
import gluon.dal
import re
from extjs import *
from extjs import (Base,
CheckBox,
ComboBox,
DirectStore,
FieldContainer,
FieldDate,
FieldDict,
FieldList,
FieldNumber,
FieldSet,
FieldText,
FieldTextArea,
FieldTime,
FormPanel,
GridColumn,
GridColumnModel,
GridFilter,
GridPanel,
GridRowNumberer,
GridTemplateColumn,
GridWithFilter,
Model,
PanelWithUrlSelector)
from gluon import current
from gluon.tools import PluginManager
from gridmodifier import ROW_NUMBERING
from helper import (encode_field,
get_field_validators,
......@@ -18,21 +42,21 @@ from mapper import map_default
# Associated gluon.dal.field type with an ExtJS widget
# The dictionary contains configuration parameters of the Ext JS widget
FTYPE_TO_XTYPE = {'boolean': (CheckBox, {}), \
'date': (FieldDate, {'format': 'Y-m-d'}), \
'datetime': (FieldDate, {'format': 'Y-m-d H:i:s'}), \
'double': (FieldNumber, {'decimalPrecision':2,
'decimalSeparator': '.'}), \
'id': (FieldText, {}), \
'integer': (FieldNumber, {}), \
FTYPE_TO_XTYPE = {'boolean': (CheckBox, {}),
'date': (FieldDate, {'format': 'Y-m-d'}),
'datetime': (FieldDate, {'format': 'Y-m-d H:i:s'}),
'double': (FieldNumber, {'decimalPrecision': 2,
'decimalSeparator': '.'}),
'id': (FieldText, {}),
'integer': (FieldNumber, {}),
'json': (FieldDict, {}),
'list:string': (FieldList, {}), \
'list:integer': (FieldTextArea, {}), \
'password': (FieldText, {'inputType': 'password'}), \
'reference': (ComboBox, {}), \
'string': (FieldText, {}), \
'text': (FieldTextArea, {}), \
'time': (FieldTime, {}), \
'list:string': (FieldList, {}),
'list:integer': (FieldTextArea, {}),
'password': (FieldText, {'inputType': 'password'}),
'reference': (ComboBox, {}),
'string': (FieldText, {}),
'text': (FieldTextArea, {}),
'time': (FieldTime, {}),
'upload': (FieldText, {'inputType': 'file'})}
......@@ -240,7 +264,8 @@ def to_field(field, composite=True, linkedcombo=True, **kwargs):
cfg.update(composite_fields[field.name].extjs)
# an embedded field already used in a composite field
elif composite and composite_fields and field.name in composite_fields.others:
elif composite \
and composite_fields and field.name in composite_fields.others:
cfg = None
# a basic field
......@@ -641,11 +666,11 @@ def to_gridPanel(table, **kwargs):
# configuration option from the keyword arguments
cfg.update(kwargs)
# grid with filter
filter = to_gridFilter(table)
if filter:
# grid with grid_filter
grid_filter = to_gridFilter(table)
if grid_filter:
cfg = GridWithFilter(panelItems=dict(cfg),
selectorItems=filter)
selectorItems=grid_filter)
if tablename in modifier_grids:
cfg.update(modifier_grids[tablename].grid_with_filter.extjs)
......@@ -672,7 +697,6 @@ def to_jsonstore(table, **kwargs):
DirectStore
"""
db = table._db
tablename = table._tablename
# NOTE: the configuration option extraParams defined the
......@@ -710,7 +734,8 @@ def to_jsonstore(table, **kwargs):
# in form and in grid.
if is_foreign_field(field):
if 'where' not in base_params: base_params['where'] = []
if 'where' not in base_params:
base_params['where'] = []
# the foreign field
k_tablename, k_fieldname, k_key = get_foreign_field(field)
......@@ -729,17 +754,17 @@ def to_jsonstore(table, **kwargs):
for el in modifier_stores[tablename].orderby:
if isinstance(el, gluon.dal.Field):
field, dir = el, 'ASC'
field, direction = el, 'ASC'
elif isinstance(el, gluon.dal.Expression):
field, dir = el.first, 'DESC'
field, direction = el.first, 'DESC'
base_params['orderby'].append([field.tablename, field.name, dir])
li = [field.tablename, field.name, direction]
base_params['orderby'].append(li)
# Ext JS configuration option from the modifier
cfg.update(modifier_stores[tablename].extjs)
# Ext JS configuration options from the keyword arguments
cfg.update(kwargs)
......@@ -773,7 +798,8 @@ def to_model(table):
# the default value can be a callable.
# that case makes no sense on the client side
# in addition the JSON dumps will failed
# therefore the default value are limited to types which can be JSONify.
# therefore the default value are limited to types which
# can be JSONifyied.
default = field.default
if default and isinstance(default, JSON_TYPES):
field_cfg['defaultValue'] = default
......@@ -896,4 +922,4 @@ def to_viewport(**kwargs):
# configuration options from the keyword arguments
cfg.update(kwargs)
return cfg
\ No newline at end of file
return cfg
""" the database service.
"""
import re
from basesvc import BaseSvc
from converter import ROOT, SUCCESS, TOTAL
from gluon import current
from gluon.contrib import simplejson as json
from gluon.storage import Storage
from helper import (decode_field,
encode_field,
......@@ -17,6 +16,7 @@ from helper import (decode_field,
is_foreign_field,
rows_serializer)
CALLBACK_ERRORS = "_callback_errors"
FIELD_NOT_IN_DB = "the field '%s.%s' doesn't exist in the database."
......@@ -39,8 +39,8 @@ REG_BRACKET = re.compile(r'\[(\w+).(\w+)\]')
REG_IS_JOIN = re.compile(r'db\.\w+\.id_(?P<ref>\w+) == db\.(?P=ref)\.id')
class DbSvcException(BaseException): pass
class DbSvcException(BaseException):
pass
class DbSvc(BaseSvc):
......@@ -104,7 +104,8 @@ class DbSvc(BaseSvc):
contains the ``keywords``.
Args:
arg (dict): :ref:`input transaction dictionary <input transaction dictionary>`
arg (dict): :ref:`input transaction dictionary
<input transaction dictionary>`
Keyword Args:
keywords (dict):
......@@ -124,7 +125,6 @@ class DbSvc(BaseSvc):
table = arg['tableName']
self._is_table_in_db(table)
def _encode_query(self, li):
"""Encode the query send by the client to a web2py ``Query``.
......@@ -132,20 +132,25 @@ class DbSvc(BaseSvc):
li (list): the query send by the client as a list of string.
The following string are understood by the method::
"[table1.field1] == [table2.field2]" or "[table1, field1] == [table2, field2]"
"[table1.field1] == [table2.field2]"
"[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])"
"([table1.field1] == [table2.field2]) |
([table1.field2] == [table2.field1])"
Returns:
tuple of gluon.dal.Query:
* the first element is the full query with all conditions or an empty string.
* the second element contains only the left join conditions or an empty string.
* the third element contains only the filter conditions or an empty string.
* the first element is the full query with all conditions
or an empty string.
* the second element contains only the left join conditions
or an empty string.
* the third element contains only the fltr conditions
or an empty string.
Note:
All elements of the list are ANDED in the web2py query
......@@ -157,7 +162,7 @@ class DbSvc(BaseSvc):
Boolean operators are ``&, |``
"""
query, join, filter = "", "", ""
query, join, fltr = "", "", ""
if not li:
return query
......@@ -179,7 +184,7 @@ class DbSvc(BaseSvc):
join = ("(%s) & (%s)" % (join, s) if join else s)
else:
filter = ("(%s) & (%s)" % (filter, s) if filter else s)
fltr = ("(%s) & (%s)" % (fltr, s) if fltr else s)
# build the query
query = ("(%s) & (%s)" % (query, s) if query else s)
......@@ -190,18 +195,17 @@ class DbSvc(BaseSvc):
if join:
join = eval(join, {}, {"db": current.globalenv['db']})
if filter:
filter = eval(filter, {}, {"db": current.globalenv['db']})
return (query, join, filter)
if fltr:
fltr = eval(fltr, {}, {"db": current.globalenv['db']})
return (query, join, fltr)
def _get_record(self, table, id):
"""Get the record ``id`` located in the database ``table``.
def _get_record(self, table, rec_id):
"""Get the record *id* located in the database ``table``.
Args:
table (gluon.dal.Table): database table
id (int): database record identifier
rec_id (int): database record identifier
Returns:
dict:
......@@ -215,18 +219,17 @@ class DbSvc(BaseSvc):
for field in db[table].fields:
key = encode_field(table, field)
record[key] = db[table][id][field]
record[key] = db[table][rec_id][field]
if is_foreign_field(db[table][field]):
k_table, k_field, k_query = get_foreign_field(db[table][field])
k_key = encode_field(k_table, k_field)
k_id = db[table][id][field]
k_id = db[table][rec_id][field]
record[k_key] = db[k_table][k_id][k_field]
return record
def _is_field_in_table(self, table, field):
"""Check that the ``field`` belongs to the ``table``.
......@@ -240,7 +243,6 @@ class DbSvc(BaseSvc):
"""
return field in current.globalenv['db'][table].fields
def _is_fields_values_valid(self, table, fields):
"""Check each ``field`` value against its ``validators``.
......@@ -253,8 +255,8 @@ class DbSvc(BaseSvc):
Returns:
dict:
key, value pairs for each field with errors.
The key is encoded as ``Table1Field1`` and the value is a string
describing the error message.
The key is encoded as ``Table1Field1`` and the
value is a string describing the error message.
"""
di = {}
......@@ -275,7 +277,6 @@ class DbSvc(BaseSvc):
return di
def _is_table_in_db(self, tablename):
"""Check that the table exists in the database.
......@@ -295,7 +296,6 @@ class DbSvc(BaseSvc):
if tablename not in tablenames:
raise DbSvcException(TABLE_NOT_IN_DB % table)
def _prepare_records(self, arg):
"""Prepare the transaction data dictionary, ``arg``, for its insertion
in the database (create or update).
......@@ -307,7 +307,8 @@ class DbSvc(BaseSvc):
The method also validates each value.
Args:
arg (dict): :ref:`input transaction dictionary <input transaction dictionary>`
arg (dict): :ref:`input transaction dictionary
<input transaction dictionary>`
Returns:
gluon.storage.Storage:
......@@ -316,8 +317,8 @@ class DbSvc(BaseSvc):
* One to one correspondence between the errors
and the records lists
* ``records`` is a list of dictionaries containing key value pairs
where the key corresponds to a database field.
* ``records`` is a list of dictionaries containing key
value pairs where the key corresponds to a database field.
The keys are encoded as ``Table1Field1``.
* The ``error`` is ``None`` when the ``record`` is validated.
......@@ -353,7 +354,6 @@ class DbSvc(BaseSvc):
return data
def count(self, tablename):
"""Count the total number of records in the table
......@@ -364,18 +364,19 @@ class DbSvc(BaseSvc):
db = current.globalenv['db']
return db(db[tablename].id > 0).count()
def create(self, arg):
"""Create new records defined by the transaction dictionary *arg*.
Several *create* transactions can be processed at the same time.
Args:
arg (dict): :ref:`input transaction dictionary <input transaction dictionary>`
arg (dict): :ref:`input transaction dictionary
<input transaction dictionary>`
Returns:
dict:
:ref:`output transaction dictionary <output transaction dictionary>`
:ref:`output transaction dictionary
<output transaction dictionary>`
The full transaction is aborted when:
* at least one field value is not validated by the
......@@ -409,14 +410,14 @@ class DbSvc(BaseSvc):
for fields in data.records:
try:
id = table.insert (**fields)
rec_id = table.insert(**fields)
# operation can be rejected by the database
except Exception as dbe:
return {SUCCESS: False, "errors": dbe.message, ROOT: fields}
# operation can be reject by callbacks table._before_insert
if not id:
if not rec_id:
msg = REJECT_BY_CALLBACK
if CALLBACK_ERRORS in table:
msg = table._callback_errors
......@@ -425,16 +426,15 @@ class DbSvc(BaseSvc):
# return the complete record
# mandatory to display the new record in the grid.
record = self._get_record(tablename, id)
record = self._get_record(tablename, rec_id)
ids.append(str(id))
ids.append(str(rec_id))
records.append(record)
txt = ', '.join(ids)
self.dbg("End DbSvc.create.", RECORDS_INSERTED % txt)
return {SUCCESS: True, 'msg': RECORDS_INSERTED % txt, ROOT: records}
def destroy(self, arg):
"""Delete existing records defined by the transaction dictionary *arg*.
......@@ -442,16 +442,19 @@ class DbSvc(BaseSvc):
Args:
arg (dict):
:ref:`input transaction dictionary <input transaction dictionary>`
The keyword ``records`` contains the list of ``id`` to be deleted
*e.g.* ``[id1, id2, ... ]``.
:ref:`input transaction dictionary
<input transaction dictionary>`
The keyword ``records`` contains the list of ``rec_id``
to be deleted *e.g.* ``[id1, id2, ... ]``.
Returns:
dict:
:ref:`output transaction dictionary <output transaction dictionary>`
:ref:`output transaction dictionary
<output transaction dictionary>`
* The keyword ``records`` is the list of dictionary containing the ``id``
of the deleted records *e.g.* ``[{TableId:xx}, ..]``.
* The keyword ``records`` is the list of dictionary
containing the ``rec_id`` of the deleted records
*e.g.* ``[{TableId:xx}, ..]``.
* The full transaction is aborted when:
- one record does not exist
......@@ -472,36 +475,41 @@ class DbSvc(BaseSvc):
table = db[tablename]
# Abort the transaction is at least one record does not exists
for id in arg[ROOT]:
if not table[id]:
txt = RECORD_NOT_IN_DB % (id, tablename)
return {"success": False, "errors": txt, ROOT: {table_id: id}}
for rec_id in arg[ROOT]:
if not table[rec_id]:
otd = {"success": False,
"errors": RECORD_NOT_IN_DB % (rec_id, tablename),
ROOT: {table_id: rec_id}}
return otd
# delete records
for id in arg[ROOT]:
for rec_id in arg[ROOT]:
try:
rep = db(table.id == id).delete()
rep = db(table.id == rec_id).delete()
# operation can be rejected by the database
except Exception as dbe:
return {SUCCESS: False, "errors": dbe.message, ROOT: fields}
otd = {SUCCESS: False,
"errors": dbe.message,
ROOT: {table_id: rec_id}}
return otd
# operation can be reject by callbacks table._before_delete
if not rep:
msg = REJECT_BY_CALLBACK
if CALLBACK_ERRORS in table:
msg = table._callback_errors
return {SUCCESS: False, "errors": msg, ROOT: {table_id: id}}
otd = {SUCCESS: False, "errors": msg, ROOT: {table_id: rec_id}}
return otd
ids.append(str(id))
records.append({table_id: id})
ids.append(str(rec_id))
records.append({table_id: rec_id})
txt = ', '.join(ids)
self.dbg("End DbSvc.destroy")
return {"success": True, "msg": RECORDS_DELETED % txt, ROOT: records}
def read(self, arg):
"""Read the content of a table as specified in the transaction
dictionary *arg*.
......@@ -522,21 +530,23 @@ class DbSvc(BaseSvc):
* the *optional* key ``orderby`` is a list of tuples to build
the order directive::
[("table1", "field1", "dir"), ("table2", "field3", "dir"), ...]
[("table1", "field1", "dir"),
("table2", "field3", "dir"), ...]
The third argument is equal to ``"ASC"`` or ``"DESC"``.
* The transaction dictionary can also contains parameters useful
for paging. For more details, see the `Ext.toolbar.Paging`_
configuration parameters: ``page``, ``start``, ``limit``,
``sort`` and ``dir``.
* The transaction dictionary can also contains parameters
useful for paging. For more details, see the
`Ext.toolbar.Paging`_ configuration parameters: ``page``,
``start``, ``limit``, ``sort`` and ``dir``.
Returns:
dict:
:ref:`output transaction dictionary <output transaction dictionary>`
:ref:`output transaction dictionary
<output transaction dictionary>`
* The keyword ``records`` is a list of dictionary containing all
key value pairs for the record found in the database.
* The keyword ``records`` is a list of dictionary containing
all key value pairs for the record found in the database.
.. include:: ../../../../docs/api/extjs_links.txt
......@@ -582,9 +592,9 @@ class DbSvc(BaseSvc):
# handle the orderby directive ['[table1, field2, dir]', ....]
if 'orderby' in arg: