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
|
||||||
import appy.gen.descriptors
|
import appy.gen.descriptors
|
||||||
from appy.gen.po import PoMessage
|
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, \
|
from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage, \
|
||||||
sequenceTypes
|
sequenceTypes
|
||||||
TABS = 4 # Number of blanks in a Python indentation.
|
TABS = 4 # Number of blanks in a Python indentation.
|
||||||
|
@ -486,6 +487,20 @@ class ArchetypesClassDescriptor(ClassDescriptor):
|
||||||
res = self.isFolder(theClass.__bases__[0])
|
res = self.isFolder(theClass.__bases__[0])
|
||||||
return res
|
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
|
@staticmethod
|
||||||
def getSearches(klass):
|
def getSearches(klass):
|
||||||
'''Returns the list of searches that are defined on this class.'''
|
'''Returns the list of searches that are defined on this class.'''
|
||||||
|
|
|
@ -615,6 +615,9 @@ class Generator(AbstractGenerator):
|
||||||
Flavour._appy_addQueryResultColumns(classDescr)
|
Flavour._appy_addQueryResultColumns(classDescr)
|
||||||
# Add the search-related fields.
|
# Add the search-related fields.
|
||||||
Flavour._appy_addSearchRelatedFields(classDescr)
|
Flavour._appy_addSearchRelatedFields(classDescr)
|
||||||
|
importMean = classDescr.getCreateMean('Import')
|
||||||
|
if importMean:
|
||||||
|
Flavour._appy_addImportRelatedFields(classDescr)
|
||||||
Flavour._appy_addWorkflowFields(self.flavourDescr)
|
Flavour._appy_addWorkflowFields(self.flavourDescr)
|
||||||
Flavour._appy_addWorkflowFields(self.podTemplateDescr)
|
Flavour._appy_addWorkflowFields(self.podTemplateDescr)
|
||||||
# Generate the flavour class and related i18n messages
|
# Generate the flavour class and related i18n messages
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
import os, os.path
|
||||||
import appy.gen
|
import appy.gen
|
||||||
from appy.gen import Type
|
from appy.gen import Type
|
||||||
from appy.gen.plone25.mixins import AbstractMixin
|
from appy.gen.plone25.mixins import AbstractMixin
|
||||||
|
@ -148,4 +149,26 @@ class FlavourMixin(AbstractMixin):
|
||||||
dAttr = self._appy_getTypeAsDict(attrName, attr, appyClass)
|
dAttr = self._appy_getTypeAsDict(attrName, attr, appyClass)
|
||||||
res.append((attrName, dAttr))
|
res.append((attrName, dAttr))
|
||||||
return res
|
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__
|
res[means.id] = means.__dict__
|
||||||
return res
|
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):
|
def onImportObjects(self):
|
||||||
'''This method is called when the user wants to create objects from
|
'''This method is called when the user wants to create objects from
|
||||||
external data.'''
|
external data.'''
|
||||||
|
|
|
@ -206,6 +206,18 @@ class Flavour(ModelClass):
|
||||||
page='userInterface', group=classDescr.klass.__name__)
|
page='userInterface', group=classDescr.klass.__name__)
|
||||||
klass._appy_addField(fieldName, fieldType, classDescr)
|
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
|
@classmethod
|
||||||
def _appy_addWorkflowFields(klass, classDescr):
|
def _appy_addWorkflowFields(klass, classDescr):
|
||||||
'''Adds, for a given p_classDescr, the workflow-related fields.'''
|
'''Adds, for a given p_classDescr, the workflow-related fields.'''
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
tal:define="appFolder context/getParentNode;
|
tal:define="appFolder context/getParentNode;
|
||||||
contentType request/type_name;
|
contentType request/type_name;
|
||||||
tool python: portal.get('portal_%s' % appFolder.id.lower());
|
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">
|
global allAreImported python:True">
|
||||||
|
|
||||||
<div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
|
<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
|
if res: return res._len # It is a LazyMap instance
|
||||||
else: return 0
|
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):
|
def reindex(self):
|
||||||
'''Asks a direct object reindexing. In most cases you don't have to
|
'''Asks a direct object reindexing. In most cases you don't have to
|
||||||
reindex objects "manually" with this method. When an object is
|
reindex objects "manually" with this method. When an object is
|
||||||
|
|
Loading…
Reference in a new issue