[gen] Class.listColumns can now be a static method (accepting the tool as single arg). [gen] Indexed Ref fields are now sortable. For every such field, 2 indexes are created: a list index for searching, and a standard index for sorting (by their title).

This commit is contained in:
Gaetan Delannay 2014-11-28 14:42:32 +01:00
parent 5c41f1b3d2
commit c11002b7d5
8 changed files with 100 additions and 66 deletions

View file

@ -32,7 +32,7 @@ class ClassDescriptor(Descriptor):
self.name = getClassName(self.klass, generator.applicationName)
self.predefined = False
self.customized = False
# Phase and page names will be calculated later, when first required.
# Phase and page names will be calculated later, when first required
self.phases = None
self.pages = None
@ -206,19 +206,26 @@ class ClassDescriptor(Descriptor):
if search.name == searchName:
return search
def addIndexMethod(self, field):
def addIndexMethod(self, field, secondary=False):
'''For indexed p_field, this method generates a method that allows to
get the value of the field as must be copied into the corresponding
index.'''
index. Some fields have a secondary index for sorting purposes. If
p_secondary is True, this method generates the method for this
secondary index.'''
m = self.methods
spaces = TABS
n = field.fieldName
m += '\n' + ' '*spaces + 'def get%s%s(self):\n' % (n[0].upper(), n[1:])
suffix = secondary and '_sort' or ''
m += '\n' + ' '*spaces + 'def get%s%s%s(self):\n' % \
(n[0].upper(), n[1:], suffix)
spaces += TABS
m += ' '*spaces + "'''Gets indexable value of field \"%s\".'''\n" % n
suffix = secondary and ', True' or ''
m += ' '*spaces + 'return self.getAppyType("%s").getIndexValue(' \
'self)\n' % n
'self%s)\n' % (n, suffix)
self.methods = m
if not secondary and field.appyType.hasSortIndex():
self.addIndexMethod(field, secondary=True)
def addField(self, fieldName, fieldType):
'''Adds a new field to the Tool.'''
@ -493,7 +500,7 @@ class TranslationClassDescriptor(ClassDescriptor):
mHeight = int(len(msgContent)/maxLine) + msgContent.count('<br/>')
height = max(height, mHeight)
if height < 1:
# This is a one-line field.
# This is a one-line field
params['width'] = width
else:
# This is a multi-line field, or a very-long-single-lined field

View file

@ -334,22 +334,21 @@ class ToolMixin(BaseMixin):
If p_refObject and p_refField are given, the query is limited to the
objects that are referenced from p_refObject through p_refField.'''
params = {'ClassName': className}
appyClass = self.getAppyClass(className, wrapper=True)
klass = self.getAppyClass(className, wrapper=True)
if not brainsOnly: params['batch'] = True
# Manage additional criteria from a search when relevant
if searchName: search = self.getSearch(className, searchName)
if search:
# Add in params search and sort criteria.
search.updateSearchCriteria(params, appyClass)
# Determine or override sort if specified.
# Add in params search and sort criteria
search.updateSearchCriteria(params, klass)
# Determine or override sort if specified
if sortBy:
params['sort_on'] = Search.getIndexName(sortBy, usage='sort')
if sortOrder == 'desc': params['sort_order'] = 'reverse'
else: params['sort_order'] = None
# If defined, add the filter among search parameters.
params['sort_on'] = Search.getIndexName(sortBy, klass, usage='sort')
params['sort_order'] = (sortOrder == 'desc') and 'reverse' or None
# If defined, add the filter among search parameters
if filterKey:
filterKey = Search.getIndexName(filterKey)
filterValue = Search.getSearchValue(filterKey,filterValue,appyClass)
filterKey = Search.getIndexName(filterKey, klass)
filterValue = Search.getSearchValue(filterKey, filterValue, klass)
params[filterKey] = filterValue
# TODO This value needs to be merged with an existing one if already
# in params, or, in a first step, we should avoid to display the
@ -367,7 +366,7 @@ class ToolMixin(BaseMixin):
self.appy().numberOfResultsPerPage
elif maxResults == 'NO_LIMIT':
maxResults = None
# Return brains only if required.
# Return brains only if required
if brainsOnly:
if not maxResults: return brains
else: return brains[:maxResults]
@ -399,7 +398,9 @@ class ToolMixin(BaseMixin):
return refInfo[0].getAppyType(refInfo[1]).shownInfo
else:
k = self.getAppyClass(className)
return hasattr(k, 'listColumns') and k.listColumns or ('title',)
if not hasattr(k, 'listColumns'): return ('title',)
if callable(k.listColumns): return k.listColumns(self.appy())
return k.listColumns
def truncateValue(self, value, width=20):
'''Truncates the p_value according to p_width. p_value has to be
@ -510,13 +511,6 @@ class ToolMixin(BaseMixin):
if role in creators:
return True
def isSortable(self, name, className, usage):
'''Is field p_name defined on p_className sortable for p_usage purposes
(p_usage can be "ref" or "search")?'''
if (',' in className) or (name == 'state'): return False
appyType = self.getAppyType(name, className=className)
if appyType: return appyType.isSortable(usage=usage)
def subTitleIsUsed(self, className):
'''Does class named p_className define a method "getSubTitle"?'''
klass = self.getAppyClass(className)

View file

@ -102,7 +102,7 @@ td.search { padding-top: 8px }
border: 1px solid grey; box-shadow: 2px 2px 2px #888888}
.dropdown { display:none; position: absolute; top: 15px; left: 1px;
border: 1px solid #cccccc; background-color: white;
padding: 3px 4px 0; font-size: 8pt; font-weight: normal;
padding: 3px 4px 3px; font-size: 8pt; font-weight: normal;
text-align: left; z-index: 2; line-height: normal }
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
.dropdown a:hover { text-decoration: underline }
@ -133,7 +133,7 @@ td.search { padding-top: 8px }
.even { background-color: #fbfbfb }
.odd { background-color: #f6f6f6 }
.odd2 { background-color: #f2f2f2 }
.refMenuItem { border-top: grey 1px dashed; margin: 3px 0; padding-top: 3px }
.refMenuItem { border-top: grey 1px dashed; margin-top: 3px; padding-top: 3px }
.summary { margin-bottom: 5px; background-color: whitesmoke;
border: 3px solid white }
.by { padding: 5px; color: grey; font-size: 97% }

View file

@ -380,11 +380,12 @@ class ToolWrapper(AbstractWrapper):
</th>
<th for="column in columns"
var2="field=column.field;
sortable=ztool.isSortable(field.name, className, 'search');
sortable=field.isSortable(usage='search');
filterable=field.filterable"
width=":column.width" align=":column.align">
<x>::ztool.truncateText(_(field.labelId))</x>
<x>:tool.pxSortAndFilter</x><x>:tool.pxShowDetails</x>
<x if="totalNumber &gt; 1">:tool.pxSortAndFilter</x>
<x>:tool.pxShowDetails</x>
</th>
</tr>

View file

@ -694,6 +694,8 @@ class AbstractWrapper(object):
n = field.name
indexName = 'get%s%s' % (n[0].upper(), n[1:])
res[indexName] = field.getIndexType()
# Add the secondary index if present
if field.hasSortIndex(): res['%s_sort' % indexName] = 'FieldIndex'
return res
# --------------------------------------------------------------------------
@ -1065,8 +1067,16 @@ class AbstractWrapper(object):
method in those cases.
'''
if fields:
# Get names of indexes from field names.
indexes = [Search.getIndexName(name) for name in fields]
# Get names of indexes from field names
indexes = []
for name in fields:
field = self.getField(name)
if not field.indexed: continue
# A field may have 2 different indexes
iName = field.getIndexName(usage='search')
indexes.append(iName)
sName = field.getIndexName(usage='sort')
if sName != iName: indexes.append(sName)
else:
indexes = None
self.o.reindex(indexes=indexes, unindex=unindex)