Added a method for computing things on large sets of objects and added the possibility to edit through-the-web in the flavour the import path for classes whose instances can be imported from a file system.
This commit is contained in:
parent
08c5abdd49
commit
24c0dee96c
|
@ -11,7 +11,8 @@ from utils import stringify
|
|||
import appy.gen
|
||||
import appy.gen.descriptors
|
||||
from appy.gen.po import PoMessage
|
||||
from appy.gen import Date, String, State, Transition, Type, Search, Selection
|
||||
from appy.gen import Date, String, State, Transition, Type, Search, \
|
||||
Selection, Import
|
||||
from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage, \
|
||||
sequenceTypes
|
||||
TABS = 4 # Number of blanks in a Python indentation.
|
||||
|
@ -486,6 +487,20 @@ class ArchetypesClassDescriptor(ClassDescriptor):
|
|||
res = self.isFolder(theClass.__bases__[0])
|
||||
return res
|
||||
|
||||
def getCreateMean(self, type='Import'):
|
||||
'''Returns the mean for this class that corresponds to p_type, or
|
||||
None if the class does not support this create mean.'''
|
||||
if not self.klass.__dict__.has_key('create'): return None
|
||||
else:
|
||||
means = self.klass.create
|
||||
if not means: return None
|
||||
if not isinstance(means, tuple) and not isinstance(means, list):
|
||||
means = [means]
|
||||
for mean in means:
|
||||
exec 'found = isinstance(mean, %s)' % type
|
||||
if found: return mean
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def getSearches(klass):
|
||||
'''Returns the list of searches that are defined on this class.'''
|
||||
|
|
|
@ -615,6 +615,9 @@ class Generator(AbstractGenerator):
|
|||
Flavour._appy_addQueryResultColumns(classDescr)
|
||||
# Add the search-related fields.
|
||||
Flavour._appy_addSearchRelatedFields(classDescr)
|
||||
importMean = classDescr.getCreateMean('Import')
|
||||
if importMean:
|
||||
Flavour._appy_addImportRelatedFields(classDescr)
|
||||
Flavour._appy_addWorkflowFields(self.flavourDescr)
|
||||
Flavour._appy_addWorkflowFields(self.podTemplateDescr)
|
||||
# Generate the flavour class and related i18n messages
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
import os, os.path
|
||||
import appy.gen
|
||||
from appy.gen import Type
|
||||
from appy.gen.plone25.mixins import AbstractMixin
|
||||
|
@ -148,4 +149,26 @@ class FlavourMixin(AbstractMixin):
|
|||
dAttr = self._appy_getTypeAsDict(attrName, attr, appyClass)
|
||||
res.append((attrName, dAttr))
|
||||
return res
|
||||
|
||||
def getImportElements(self, contentType):
|
||||
'''Returns the list of elements that can be imported from p_path for
|
||||
p_contentType.'''
|
||||
tool = self.getParentNode()
|
||||
appyClass = tool.getAppyClass(contentType)
|
||||
importParams = tool.getCreateMeans(appyClass)['import']
|
||||
onElement = importParams['onElement'].__get__('')
|
||||
sortMethod = importParams['sort']
|
||||
if sortMethod: sortMethod = sortMethod.__get__('')
|
||||
elems = []
|
||||
importPath = getattr(self, 'importPathFor%s' % contentType)
|
||||
for elem in os.listdir(importPath):
|
||||
elemFullPath = os.path.join(importParams['path'], elem)
|
||||
elemInfo = onElement(elemFullPath)
|
||||
if elemInfo:
|
||||
elemInfo.insert(0, elemFullPath) # To the result, I add the full
|
||||
# path of the elem, which will not be shown.
|
||||
elems.append(elemInfo)
|
||||
if sortMethod:
|
||||
elems = sortMethod(elems)
|
||||
return [importParams['headers'], elems]
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -364,26 +364,6 @@ class ToolMixin(AbstractMixin):
|
|||
res[means.id] = means.__dict__
|
||||
return res
|
||||
|
||||
def getImportElements(self, contentType):
|
||||
'''Returns the list of elements that can be imported from p_path for
|
||||
p_contentType.'''
|
||||
appyClass = self.getAppyClass(contentType)
|
||||
importParams = self.getCreateMeans(appyClass)['import']
|
||||
onElement = importParams['onElement'].__get__('')
|
||||
sortMethod = importParams['sort']
|
||||
if sortMethod: sortMethod = sortMethod.__get__('')
|
||||
elems = []
|
||||
for elem in os.listdir(importParams['path']):
|
||||
elemFullPath = os.path.join(importParams['path'], elem)
|
||||
elemInfo = onElement(elemFullPath)
|
||||
if elemInfo:
|
||||
elemInfo.insert(0, elemFullPath) # To the result, I add the full
|
||||
# path of the elem, which will not be shown.
|
||||
elems.append(elemInfo)
|
||||
if sortMethod:
|
||||
elems = sortMethod(elems)
|
||||
return [importParams['headers'], elems]
|
||||
|
||||
def onImportObjects(self):
|
||||
'''This method is called when the user wants to create objects from
|
||||
external data.'''
|
||||
|
|
|
@ -206,6 +206,18 @@ class Flavour(ModelClass):
|
|||
page='userInterface', group=classDescr.klass.__name__)
|
||||
klass._appy_addField(fieldName, fieldType, classDescr)
|
||||
|
||||
@classmethod
|
||||
def _appy_addImportRelatedFields(klass, classDescr):
|
||||
'''Adds, for class p_classDescr, attributes related to the import
|
||||
functionality for class p_classDescr.'''
|
||||
className = classDescr.name
|
||||
# Field that defines the path of the files to import.
|
||||
fieldName = 'importPathFor%s' % className
|
||||
defValue = classDescr.getCreateMean('Import').path
|
||||
fieldType = String(page='data', multiplicity=(1,1), default=defValue,
|
||||
group=classDescr.klass.__name__)
|
||||
klass._appy_addField(fieldName, fieldType, classDescr)
|
||||
|
||||
@classmethod
|
||||
def _appy_addWorkflowFields(klass, classDescr):
|
||||
'''Adds, for a given p_classDescr, the workflow-related fields.'''
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
tal:define="appFolder context/getParentNode;
|
||||
contentType request/type_name;
|
||||
tool python: portal.get('portal_%s' % appFolder.id.lower());
|
||||
importElems python: tool.getImportElements(contentType);
|
||||
flavour python: tool.getFlavour(contentType);
|
||||
importElems python: flavour.getImportElements(contentType);
|
||||
global allAreImported python:True">
|
||||
|
||||
<div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
|
||||
|
|
|
@ -295,6 +295,42 @@ class AbstractWrapper:
|
|||
if res: return res._len # It is a LazyMap instance
|
||||
else: return 0
|
||||
|
||||
def compute(self, klass, context=None, expression=None, noSecurity=False,
|
||||
**fields):
|
||||
'''This method, like m_search and m_count above, performs a query on
|
||||
objects of p_klass. But in this case, instead of returning a list of
|
||||
matching objects (like m_search) or counting elements (like p_count),
|
||||
it evaluates, on every matching object, a Python p_expression (which
|
||||
may be an expression or a statement), and returns, if needed, a
|
||||
result. The result may be initialized through parameter p_context.
|
||||
p_expression is evaluated with 2 variables in its context: "obj"
|
||||
which is the currently walked object, instance of p_klass, and "ctx",
|
||||
which is the context as initialized (or not) by p_context. p_context
|
||||
may be used as
|
||||
(1) a variable that is updated on every call to produce a result;
|
||||
(2) an input variable;
|
||||
(3) both.
|
||||
|
||||
The method returns p_context, modified or not by evaluation of
|
||||
p_expression on every matching object.
|
||||
|
||||
When you need to perform an action or computation on a lot of
|
||||
objects, use this method instead of doing things like
|
||||
|
||||
"for obj in self.search(MyClass,...)"
|
||||
'''
|
||||
flavour = self.flavour
|
||||
contentType = flavour.o.getPortalType(klass)
|
||||
search = Search('customSearch', **fields)
|
||||
# Initialize the context variable "ctx"
|
||||
ctx = context
|
||||
for brain in self.tool.o.executeQuery(contentType, flavour.number, \
|
||||
search=search, brainsOnly=True, noSecurity=noSecurity):
|
||||
# Get the Appy object from the brain
|
||||
obj = brain.getObject().appy()
|
||||
exec expression
|
||||
return ctx
|
||||
|
||||
def reindex(self):
|
||||
'''Asks a direct object reindexing. In most cases you don't have to
|
||||
reindex objects "manually" with this method. When an object is
|
||||
|
|
Loading…
Reference in a new issue