[Erp5-report] r16126 - in /erp5/trunk/products/ERP5Catalog: ./ Document/ PropertySheet/ Too...
nobody at svn.erp5.org
nobody at svn.erp5.org
Thu Sep 6 17:14:51 CEST 2007
Author: aurel
Date: Thu Sep 6 17:14:50 2007
New Revision: 16126
URL: http://svn.erp5.org?rev=16126&view=rev
Log:
initial upload of archive tool
Added:
erp5/trunk/products/ERP5Catalog/Document/Archive.py (with props)
erp5/trunk/products/ERP5Catalog/PropertySheet/Archive.py (with props)
erp5/trunk/products/ERP5Catalog/PropertySheet/CatalogPreference.py (with props)
erp5/trunk/products/ERP5Catalog/Tool/
erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.py (with props)
erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.pyc (with props)
erp5/trunk/products/ERP5Catalog/Tool/__init__.py (with props)
erp5/trunk/products/ERP5Catalog/dtml/explainArchiveTool.dtml (with props)
erp5/trunk/products/ERP5Catalog/tests/testArchive.py (with props)
Modified:
erp5/trunk/products/ERP5Catalog/CatalogTool.py
erp5/trunk/products/ERP5Catalog/__init__.py
Modified: erp5/trunk/products/ERP5Catalog/CatalogTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/CatalogTool.py?rev=16126&r1=16125&r2=16126&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Catalog/CatalogTool.py (original)
+++ erp5/trunk/products/ERP5Catalog/CatalogTool.py Thu Sep 6 17:14:50 2007
@@ -50,7 +50,7 @@
from Products.ERP5Security.ERP5UserManager import SUPER_USER
import os, time, urllib, warnings
-from zLOG import LOG, PROBLEM
+from zLOG import LOG, PROBLEM, INFO
SECURITY_USING_NUX_USER_GROUPS, SECURITY_USING_PAS = range(2)
try:
@@ -74,6 +74,9 @@
except ImportError:
pass
+from Persistence import Persistent
+from Acquisition import Implicit
+
def getSecurityProduct(acl_users):
"""returns the security used by the user folder passed.
(NuxUserGroup, ERP5Security, or None if anything else).
@@ -82,6 +85,7 @@
return SECURITY_USING_PAS
elif acl_users.meta_type == NUG_meta_type:
return SECURITY_USING_NUX_USER_GROUPS
+
class IndexableObjectWrapper(CMFCoreIndexableObjectWrapper):
@@ -187,6 +191,7 @@
id = 'portal_catalog'
meta_type = 'ERP5 Catalog'
security = ClassSecurityInfo()
+
default_result_limit = 1000
manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' },
@@ -204,6 +209,29 @@
, 'manage_schema' )
manage_schema = DTMLFile( 'dtml/manageSchema', globals() )
+ def getPreferredSQLCatalogId(self, id=None):
+ """
+ Get the SQL Catalog from preference.
+ """
+ if id is None:
+ # Check if we want to use an archive
+ #if getattr(aq_base(self.portal_preferences), 'uid', None) is not None:
+ archive_path = self.portal_preferences.getPreferredArchive(sql_catalog_id=self.default_sql_catalog_id)
+ if archive_path not in ('', None):
+ try:
+ archive = self.restrictedTraverse(archive_path)
+ except KeyError:
+ # Do not fail if archive object has been removed,
+ # but preference is not up to date
+ return None
+ if archive is not None:
+ catalog_id = archive.getCatalogId()
+ if catalog_id not in ('', None):
+ return catalog_id
+ return None
+ else:
+ return id
+
security.declareProtected( 'Import/Export objects', 'addDefaultSQLMethods' )
def addDefaultSQLMethods(self, config_id='erp5'):
"""
@@ -401,7 +429,7 @@
security.declarePublic( 'getAllowedRolesAndUsers' )
- def getAllowedRolesAndUsers(self, **kw):
+ def getAllowedRolesAndUsers(self, sql_catalog_id=None, **kw):
"""
Return allowed roles and users.
@@ -419,7 +447,7 @@
user_is_superuser = (user_str == SUPER_USER)
allowedRolesAndUsers = self._listAllowedRolesAndUsers(user)
role_column_dict = {}
- column_map = self.getSQLCatalog().getColumnMap()
+ column_map = self.getSQLCatalog(sql_catalog_id).getColumnMap()
# Patch for ERP5 by JP Smets in order
# to implement worklists and search of local roles
@@ -464,7 +492,7 @@
return allowedRolesAndUsers, role_column_dict
- def getSecurityUidListAndRoleColumnDict(self, **kw):
+ def getSecurityUidListAndRoleColumnDict(self, sql_catalog_id=None, **kw):
"""
Return a list of security Uids and a dictionnary containing available
role columns.
@@ -474,7 +502,7 @@
catalogs.
"""
allowedRolesAndUsers, role_column_dict = self.getAllowedRolesAndUsers(**kw)
- catalog = self.getSQLCatalog()
+ catalog = self.getSQLCatalog(sql_catalog_id)
method = getattr(catalog, catalog.sql_search_security, None)
if method is None:
raise DeprecationWarning, "The usage of allowedRolesAndUsers is "\
@@ -501,7 +529,7 @@
return security_uid_list, role_column_dict
security.declarePublic( 'getSecurityQuery' )
- def getSecurityQuery(self, query=None, **kw):
+ def getSecurityQuery(self, query=None, sql_catalog_id=None, **kw):
"""
Build a query based on allowed roles or on a list of security_uid
values. The query takes into account the fact that some roles are
@@ -509,10 +537,10 @@
"""
original_query = query
try:
- security_uid_list, role_column_dict = self.getSecurityUidListAndRoleColumnDict(**kw)
+ security_uid_list, role_column_dict = self.getSecurityUidListAndRoleColumnDict(sql_catalog_id=sql_catalog_id, **kw)
except DeprecationWarning, message:
warnings.warn(message, DeprecationWarning)
- allowedRolesAndUsers, role_column_dict = self.getAllowedRolesAndUsers(**kw)
+ allowedRolesAndUsers, role_column_dict = self.getAllowedRolesAndUsers(sql_catalog_id=sql_catalog_id, **kw)
if role_column_dict:
query_list = []
for key, value in role_column_dict.items():
@@ -557,9 +585,13 @@
kw[ 'effective' ] = { 'query' : now, 'range' : 'max' }
kw[ 'expires' ] = { 'query' : now, 'range' : 'min' }
- query = self.getSecurityQuery(query=query, **kw)
+ catalog_id = self.getPreferredSQLCatalogId(kw.pop("sql_catalog_id", None))
+ query = self.getSecurityQuery(query=query, sql_catalog_id=catalog_id, **kw)
kw.setdefault('limit', self.default_result_limit)
- return ZCatalog.searchResults(self, query=query, **kw)
+ # get catalog from preference
+ #LOG("searchResult", INFO, catalog_id)
+ # LOG("searchResult", INFO, ZCatalog.searchResults(self, query=query, sql_catalog_id=catalog_id, src__=1, **kw))
+ return ZCatalog.searchResults(self, query=query, sql_catalog_id=catalog_id, **kw)
__call__ = searchResults
@@ -609,10 +641,11 @@
# now = DateTime()
# #kw[ 'effective' ] = { 'query' : now, 'range' : 'max' }
# #kw[ 'expires' ] = { 'query' : now, 'range' : 'min' }
-
- query = self.getSecurityQuery(query=query, **kw)
+ catalog_id = self.getPreferredSQLCatalogId(kw.pop("sql_catalog_id", None))
+ query = self.getSecurityQuery(query=query, sql_catalog_id=catalog_id, **kw)
kw.setdefault('limit', self.default_result_limit)
- return ZCatalog.countResults(self, query=query, **kw)
+ # get catalog from preference
+ return ZCatalog.countResults(self, query=query, sql_catalog_id=catalog_id, **kw)
security.declarePrivate('unrestrictedCountResults')
def unrestrictedCountResults(self, REQUEST=None, **kw):
@@ -824,4 +857,6 @@
+
+
InitializeClass(CatalogTool)
Added: erp5/trunk/products/ERP5Catalog/Document/Archive.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/Document/Archive.py?rev=16126&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Catalog/Document/Archive.py (added)
+++ erp5/trunk/products/ERP5Catalog/Document/Archive.py Thu Sep 6 17:14:50 2007
@@ -1,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+# Aurélien Calonne <aurel at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import PropertySheet, Permissions, Interface
+from Globals import InitializeClass
+from Products.ERP5.Document.Predicate import Predicate
+
+
+class Archive(Predicate):
+ """
+ A Catalog Archive object
+
+ It defines the date of the archive and the catalog to use
+ """
+
+ meta_type = 'ERP5 Archive'
+ portal_type = 'Archive'
+ isPortalContent = 1
+ isRADContent = 1
+
+ # Declarative security
+ security = ClassSecurityInfo()
+ security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+ # Declarative interfaces
+ __implements__ = ( Interface.Predicate, )
+
+ # Default Properties
+ property_sheets = ( PropertySheet.Base
+ , PropertySheet.XMLObject
+ , PropertySheet.Archive
+ )
+
+ isIndexable = 1
+
+
+InitializeClass(Archive)
Propchange: erp5/trunk/products/ERP5Catalog/Document/Archive.py
------------------------------------------------------------------------------
svn:executable = *
Added: erp5/trunk/products/ERP5Catalog/PropertySheet/Archive.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/PropertySheet/Archive.py?rev=16126&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Catalog/PropertySheet/Archive.py (added)
+++ erp5/trunk/products/ERP5Catalog/PropertySheet/Archive.py Thu Sep 6 17:14:50 2007
@@ -1,0 +1,61 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+# Aurélien Calonne <aurel at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+class Archive:
+ """
+ """
+ _properties = (
+ { 'id' : 'catalog_id',
+ 'description' : 'The id of the catalog used by the archive',
+ 'type' : 'string',
+ 'mode' : 'w' },
+ { 'id' : 'connection_id',
+ 'description' : 'The id of the connection used by the archive',
+ 'type' : 'string',
+ 'mode' : 'w' },
+ { 'id' : 'deferred_connection_id',
+ 'description' : 'The id of the deferred connection used by the archive',
+ 'type' : 'string',
+ 'mode' : 'w' },
+ { 'id' : 'priority',
+ 'description' : 'Priority of activity use to index object into the archive',
+ 'type' : 'int',
+ 'mode' : 'w' ,
+ 'default' : 5},
+ { 'id' : 'stop_date',
+ 'description' : 'The stop date at which we archive document',
+ 'type' : 'date',
+ 'range' : True,
+ 'default' : None,
+ 'mode' : 'w' },
+ { 'id' : 'inventory_method_id',
+ 'description' : 'The method that will be used to create inventory when creating archive',
+ 'type' : 'string',
+ 'mode' : 'w' },
+ )
+
Propchange: erp5/trunk/products/ERP5Catalog/PropertySheet/Archive.py
------------------------------------------------------------------------------
svn:executable = *
Added: erp5/trunk/products/ERP5Catalog/PropertySheet/CatalogPreference.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/PropertySheet/CatalogPreference.py?rev=16126&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Catalog/PropertySheet/CatalogPreference.py (added)
+++ erp5/trunk/products/ERP5Catalog/PropertySheet/CatalogPreference.py Thu Sep 6 17:14:50 2007
@@ -1,0 +1,44 @@
+#############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+# Aurélien Calonne <aurel at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+
+class CatalogPreference:
+ """
+ This property sheet defines the user preference for catalog.
+ """
+
+ _properties = (
+ { 'id' : 'preferred_archive',
+ 'description' : 'The archive the user want to use',
+ 'type' : 'string',
+ 'preference' : 1,
+ 'mode' : '' },
+ )
+
+
+
Propchange: erp5/trunk/products/ERP5Catalog/PropertySheet/CatalogPreference.py
------------------------------------------------------------------------------
svn:executable = *
Added: erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.py?rev=16126&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.py (added)
+++ erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.py Thu Sep 6 17:14:50 2007
@@ -1,0 +1,227 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+# Aurelien Calonne <aurel at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+
+from AccessControl import ClassSecurityInfo
+from Globals import InitializeClass, DTMLFile
+from Products.ERP5Type.Tool.BaseTool import BaseTool
+from Products.ERP5Type import Permissions
+from Products.ERP5Type.Cache import CachingMethod, clearCache
+from Products.ERP5Catalog import _dtmldir
+from zLOG import LOG, INFO
+
+class ArchiveTool(BaseTool):
+ """
+ Archive Tool contains archive objects
+ """
+ title = 'Archive Tool'
+ id = 'portal_archives'
+ meta_type = 'ERP5 Archive Tool'
+ portal_type = 'Archive Tool'
+ allowed_types = ('ERP5 Archive',)
+
+ # Declarative Security
+ security = ClassSecurityInfo()
+
+ security.declareProtected(Permissions.ManagePortal, 'manage_overview' )
+
+ manage_overview = DTMLFile( 'explainArchiveTool', _dtmldir)
+
+ def getSQLCatalogIdList(self):
+ """
+ Wrapper to CatalogTool method
+ """
+ return self.portal_catalog.getSQLCatalogIdList()
+
+ def SQLConnectionIDs(self):
+ """
+ Wrapper to CatalogTool method
+ """
+ return self.portal_catalog.SQLConnectionIDs()
+
+ def getArchiveIdList(self):
+ """
+ Return list of usable archive displayed to user
+ """
+ return ["%s - %s" %(x.getId(), x.getTitle()) for x in \
+ self.portal_catalog(portal_type="Archive",
+ validation_state="ready")]
+
+
+ def getArchiveList(self):
+ """
+ Return the list of archive use by catalog
+ """
+ def _getArchiveList():
+ return [x.getPath() for x in self.objectValues() if x.getValidationState() == "validated"]
+
+ # getArchiveList = CachingMethod(_getArchiveList,
+ # id='getArchiveList',
+ # cache_factory='erp5_content_short')
+
+ return _getArchiveList()
+
+
+ def manage_archive(self, destination_archive_id,
+ archive_id,
+ update_destination_sql_catalog=None,
+ update_archive_sql_catalog=None,
+ clear_destination_sql_catalog=None,
+ clear_archive_sql_catalog=None,
+ REQUEST=None, RESPONSE=None):
+ """
+ This method is used to populate an archive from the current catalog
+ It is base on hot reindexing, we start from a current catalog
+ in order to create a new current catalog plus an archive catalog.
+ Archives are defined in portal_archives, they are predicate thus
+ we use test method to know in which catalog objects must go.
+ At the end it creates inventories in order to have
+ consistent data within the new catalog
+ """
+ # First check parameter for destination catalog
+ if destination_archive_id == archive_id:
+ raise ValueError, "Archive and destination archive can't be the same"
+ portal_catalog =self.portal_catalog
+ # Guess connection id from current catalog
+ source_catalog = portal_catalog.getSQLCatalog()
+ source_catalog_id = source_catalog.getId()
+ source_connection_id = None
+ source_deferred_connection_id = None
+ for method in source_catalog.objectValues():
+ if method.meta_type == "Z SQL Method":
+ if 'deferred' in method.connection_id:
+ source_deferred_connection_id = method.connection_id
+ elif 'transactionless' not in method.connection_id:
+ source_connection_id = method.connection_id
+ if source_connection_id is not None and \
+ source_deferred_connection_id is not None:
+ break
+
+ if source_connection_id is None or source_deferred_connection_id is None:
+ raise ValueError, "Unable to determine connection id for the current catalog"
+
+ # Get destination property from archive
+ destination_archive_id = destination_archive_id.split(' - ')[0]
+ destination_archive = self._getOb(destination_archive_id)
+ destination_sql_catalog_id = destination_archive.getCatalogId()
+ destination_connection_id = destination_archive.getConnectionId()
+ destination_deferred_connection_id = destination_archive.getDeferredConnectionId()
+
+ # Get archive property from archive
+ archive_id = archive_id.split(' - ')[0]
+ archive = self._getOb(archive_id)
+ archive_sql_catalog_id = archive.getCatalogId()
+ archive_connection_id = archive.getConnectionId()
+ archive_deferred_connection_id = archive.getDeferredConnectionId()
+
+ # Check we don't use same connection id for source and destination
+ if destination_sql_catalog_id == source_catalog_id:
+ raise ValueError, "Destination and source catalog can't be the same"
+ if destination_connection_id == source_connection_id:
+ raise ValueError, "Destination and source connection can't be the same"
+ if destination_deferred_connection_id == source_deferred_connection_id:
+ raise ValueError, "Destination and source deferred connection can't be the same"
+ # Same for source and archive
+ if archive_sql_catalog_id == source_catalog_id:
+ raise ValueError, "Archive and source catalog can't be the same"
+ if archive_connection_id == source_connection_id:
+ raise ValueError, "Archive and source connection can't be the same"
+ if archive_deferred_connection_id == source_deferred_connection_id:
+ raise ValueError, "Archive and source deferred connection can't be the same"
+ # Same for destination and archive
+ if archive_sql_catalog_id == destination_sql_catalog_id:
+ raise ValueError, "Archive and destination catalog can't be the same"
+ if archive_connection_id == destination_connection_id:
+ raise ValueError, "Archive and destination connection can't be the same"
+ if archive_deferred_connection_id == destination_deferred_connection_id:
+ raise ValueError, "Archive and destination deferred connection can't be the same"
+
+ # Update connection id in destination and archive catalog if asked
+ destination_sql_catalog = getattr(portal_catalog, destination_sql_catalog_id)
+ if update_destination_sql_catalog:
+ sql_connection_id_dict = {source_connection_id : destination_connection_id,
+ source_deferred_connection_id : destination_deferred_connection_id}
+ portal_catalog.changeSQLConnectionIds(destination_sql_catalog,
+ sql_connection_id_dict)
+
+ archive_sql_catalog = getattr(portal_catalog, archive_sql_catalog_id)
+ if update_archive_sql_catalog:
+ sql_connection_id_dict = {source_connection_id : archive_connection_id,
+ source_deferred_connection_id : archive_deferred_connection_id}
+ portal_catalog.changeSQLConnectionIds(archive_sql_catalog,
+ sql_connection_id_dict)
+
+ # Clear destination and archive catalog if asked
+ if clear_destination_sql_catalog:
+ portal_catalog.manage_catalogClear(sql_catalog_id=destination_sql_catalog_id)
+ if clear_archive_sql_catalog:
+ portal_catalog.manage_catalogClear(sql_catalog_id=archive_sql_catalog_id)
+
+ # validate archive
+ archive.validate()
+ destination_archive.validate()
+
+ # Call hot reindexing
+ portal_catalog.manage_hotReindexAll(source_sql_catalog_id=source_catalog_id,
+ destination_sql_catalog_id=destination_sql_catalog_id,
+ archive=archive,
+ source_sql_connection_id_list=[source_connection_id, source_deferred_connection_id],
+ destination_sql_connection_id_list=[destination_connection_id, destination_deferred_connection_id],
+ REQUEST=REQUEST, RESPONSE=RESPONSE)
+
+ # Create inventory just before finish of hot reindexing
+ inventory_date = "%s 23:59:59" %str(archive.getStopDateRangeMax().Date())
+ LOG("inventory_date", 300, inventory_date)
+ self.activate(passive_commit=1,
+ after_method_id=('playBackRecordedObjectList'),
+ priority=5).runInventoryMethod(archive.id,
+ source_connection_id,
+ destination_sql_catalog_id,
+ inventory_date
+ )
+
+
+ if RESPONSE is not None:
+ URL1 = REQUEST.get('URL1')
+ RESPONSE.redirect(URL1 + '/portal_archives?portal_status_message=Archiving%20Started')
+
+
+ def runInventoryMethod(self, archive_id, source_connection_id, destination_sql_catalog_id, inventory_date):
+ """
+ Use a specific method to create inventory in order to use
+ activity to execute it
+ """
+ #destination_sql_catalog = getattr(self.portal_catalog, destination_sql_catalog_id)
+ archive = self._getOb(archive_id)
+ inventory_method_id = archive.getInventoryMethodId()
+ inventory_method = getattr(archive, inventory_method_id, None)
+ if inventory_method is not None:
+ inventory_method(source_connection_id, destination_sql_catalog_id, inventory_date, tag='runInventoryMethod')
+
+
+InitializeClass(ArchiveTool)
Propchange: erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.py
------------------------------------------------------------------------------
svn:executable = *
Added: erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.pyc
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.pyc?rev=16126&view=auto
==============================================================================
Binary file - no diff available.
Propchange: erp5/trunk/products/ERP5Catalog/Tool/ArchiveTool.pyc
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: erp5/trunk/products/ERP5Catalog/Tool/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/Tool/__init__.py?rev=16126&view=auto
==============================================================================
(empty)
Propchange: erp5/trunk/products/ERP5Catalog/Tool/__init__.py
------------------------------------------------------------------------------
svn:executable = *
Modified: erp5/trunk/products/ERP5Catalog/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/__init__.py?rev=16126&r1=16125&r2=16126&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Catalog/__init__.py (original)
+++ erp5/trunk/products/ERP5Catalog/__init__.py Thu Sep 6 17:14:50 2007
@@ -37,9 +37,11 @@
document_classes = updateGlobals( this_module, globals(), permissions_module = Permissions)
# Define object classes and tools
+from Tool import ArchiveTool
import CatalogTool
object_classes = ()
-portal_tools = (CatalogTool.CatalogTool,)
+portal_tools = (CatalogTool.CatalogTool,
+ ArchiveTool.ArchiveTool)
content_classes = ()
content_constructors = ()
Added: erp5/trunk/products/ERP5Catalog/dtml/explainArchiveTool.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/dtml/explainArchiveTool.dtml?rev=16126&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Catalog/dtml/explainArchiveTool.dtml (added)
+++ erp5/trunk/products/ERP5Catalog/dtml/explainArchiveTool.dtml Thu Sep 6 17:14:50 2007
@@ -1,0 +1,10 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<h3> <code>portal_archives</code> Tool </h3>
+
+<p> This tool is used to stored archives object which are predicate
+that tells in which catalog an object must go.
+</p>
+
+<dtml-var manage_page_footer>
Propchange: erp5/trunk/products/ERP5Catalog/dtml/explainArchiveTool.dtml
------------------------------------------------------------------------------
svn:executable = *
Added: erp5/trunk/products/ERP5Catalog/tests/testArchive.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Catalog/tests/testArchive.py?rev=16126&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Catalog/tests/testArchive.py (added)
+++ erp5/trunk/products/ERP5Catalog/tests/testArchive.py Thu Sep 6 17:14:50 2007
@@ -1,0 +1,312 @@
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+# Aurélien Calonne <aurel at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+# Needed in order to have a log file inside the current folder
+os.environ['EVENT_LOG_FILE'] = os.path.join(os.getcwd(), 'zLOG.log')
+os.environ['EVENT_LOG_SEVERITY'] = '-300'
+
+from Testing import ZopeTestCase
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from AccessControl import getSecurityManager
+from AccessControl.SecurityManagement import newSecurityManager
+from zLOG import LOG
+from DateTime import DateTime
+from Products.CMFCore.tests.base.testcase import LogInterceptor
+from Testing.ZopeTestCase.PortalTestCase import PortalTestCase
+from Products.ERP5Type.tests.utils import createZODBPythonScript
+from Products.ZSQLCatalog.ZSQLCatalog import HOT_REINDEXING_FINISHED_STATE,\
+ HOT_REINDEXING_RECORDING_STATE, HOT_REINDEXING_DOUBLE_INDEXING_STATE
+from Products.CMFActivity.Errors import ActivityFlushError
+from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
+from Products.ERP5.tests.testInventoryAPI import InventoryAPITestCase
+from DateTime import DateTime
+
+try:
+ from transaction import get as get_transaction
+except ImportError:
+ pass
+
+class TestArchive(InventoryAPITestCase):
+ """
+ Tests for Archive.
+ """
+
+ def getTitle(self):
+ return "ERP5Archive"
+
+ def getBusinessTemplateList(self):
+ return ('erp5_base',
+ 'erp5_trade',
+ 'erp5_apparel',
+ 'erp5_dummy_movement',
+ 'erp5_archive',
+ )
+
+ # Different variables used for this test
+ run_all_test = 0
+ quiet = 1
+
+ def afterSetUp(self):
+ self.login()
+ InventoryAPITestCase.afterSetUp(self)
+ # make sure there is no message any more
+ self.tic()
+
+ def beforeTearDown(self):
+ for module in [ self.getPersonModule(),
+ self.getOrganisationModule(),
+ self.getCategoryTool().region,
+ self.getCategoryTool().group ]:
+ module.manage_delObjects(list(module.objectIds()))
+ self.getPortal().portal_activities.manageClearActivities()
+ get_transaction().commit()
+
+ def login(self):
+ uf = self.getPortal().acl_users
+ uf._doAddUser('seb', '', ['Manager'], [])
+ user = uf.getUserById('seb').__of__(uf)
+ newSecurityManager(None, user)
+
+ def getSQLPathList(self,connection_id=None):
+ """
+ Give the full list of path in the catalog
+ """
+ if connection_id is None:
+ sql_connection = self.getSQLConnection()
+ else:
+ sql_connection = getattr(self.getPortal(),connection_id)
+ sql = 'select path from catalog'
+ result = sql_connection.manage_test(sql)
+ path_list = map(lambda x: x['path'],result)
+ return path_list
+
+ def checkRelativeUrlInSQLPathList(self,url_list,connection_id=None):
+ path_list = self.getSQLPathList(connection_id=connection_id)
+ portal_id = self.getPortalId()
+ for url in url_list:
+ path = '/' + portal_id + '/' + url
+ #LOG('checkRelativeUrlInSQLPathList found path:',0,path)
+ self.failUnless(path in path_list)
+
+ def checkRelativeUrlNotInSQLPathList(self,url_list,connection_id=None):
+ path_list = self.getSQLPathList(connection_id=connection_id)
+ portal_id = self.getPortalId()
+ for url in url_list:
+ path = '/' + portal_id + '/' + url
+ #LOG('checkRelativeUrlInSQLPathList not found path:',0,path)
+ self.failUnless(path not in path_list)
+
+ def test_Archive(self, quiet=quiet, run=1): #run_all_test):
+ if not run: return
+ if not quiet:
+ message = 'Archive'
+ ZopeTestCase._print('\n%s ' % message)
+ LOG('Testing... ',0,message)
+
+ portal = self.getPortal()
+ portal_category = self.getCategoryTool()
+ portal_activities = self.getActivityTool()
+ portal_archive = self.getArchiveTool()
+ portal_catalog = self.getCatalogTool()
+ # Create some objects
+ self.base_category = portal_category.newContent(portal_type='Base Category',
+ title="GreatTitle1")
+ module = portal.getDefaultModule('Organisation')
+ self.organisation = module.newContent(portal_type='Organisation',
+ title="GreatTitle2")
+ getInventory = self.getSimulationTool().getInventory
+ self.mvt = self._makeMovement(quantity=100, stop_date=DateTime("2006/06/06"),
+ simulation_state='delivered',)
+ self.assertEquals(100, getInventory(node_uid=self.node.getUid()))
+ self.assertEqual(len(self.folder.searchFolder(portal_type="Dummy Movement")), 1)
+ # Flush message queue
+ get_transaction().commit()
+ self.tic()
+
+ # Check well in catalog
+ self.original_connection_id = 'erp5_sql_connection'
+ self.original_deferred_connection_id = 'erp5_sql_deferred_connection'
+ path_list = [self.organisation.getRelativeUrl()]
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.original_connection_id)
+
+ # Create new connectors for destination
+ self.new_connection_id = 'erp5_sql_connection1'
+ portal.manage_addZMySQLConnection(self.new_connection_id,'',
+ 'test2 test2')
+ new_connection = portal[self.new_connection_id]
+ new_connection.manage_open_connection()
+ # the deferred one
+ self.new_deferred_connection_id = 'erp5_sql_connection2'
+ portal.manage_addZMySQLConnection(self.new_deferred_connection_id,'',
+ 'test2 test2')
+ new_deferred_connection = portal[self.new_deferred_connection_id]
+ new_deferred_connection.manage_open_connection()
+
+ # Create new connectors for archive
+ self.archive_connection_id = 'erp5_sql_connection3'
+ portal.manage_addZMySQLConnection(self.archive_connection_id,'',
+ 'test3 test3')
+ archive_connection = portal[self.archive_connection_id]
+ archive_connection.manage_open_connection()
+ # the deferred one
+ self.archive_deferred_connection_id = 'erp5_sql_connection4'
+ portal.manage_addZMySQLConnection(self.archive_deferred_connection_id,'',
+ 'test3 test3')
+ archive_deferred_connection = portal[self.archive_deferred_connection_id]
+ archive_deferred_connection.manage_open_connection()
+
+ # Create new catalog for destination
+ self.original_catalog_id = 'erp5_mysql_innodb'
+ self.new_catalog_id = self.original_catalog_id + '_2'
+ cp_data = portal_catalog.manage_copyObjects(ids=('erp5_mysql_innodb',))
+ new_id = portal_catalog.manage_pasteObjects(cp_data)[0]['new_id']
+ new_catalog_id = 'erp5_mysql_innodb_2'
+ portal_catalog.manage_renameObject(id=new_id,new_id=new_catalog_id)
+ dest_catalog = portal_catalog[new_catalog_id]
+
+ # Create new catalog for archive
+ self.archive_catalog_id = self.original_catalog_id + '_archive'
+ cp_data = portal_catalog.manage_copyObjects(ids=('erp5_mysql_innodb',))
+ archive_id = portal_catalog.manage_pasteObjects(cp_data)[0]['new_id']
+ archive_catalog_id = 'erp5_mysql_innodb_archive'
+ portal_catalog.manage_renameObject(id=archive_id,new_id=archive_catalog_id)
+ archive_catalog = portal_catalog[archive_catalog_id]
+
+ # Create an archive
+ archive = portal_archive.newContent(portal_typ="Archive",
+ catalog_id=self.archive_catalog_id,
+ connection_id=self.archive_connection_id,
+ deferred_connection_id=self.archive_deferred_connection_id,
+ priority=3,
+ inventory_method_id='Archive_createAllInventory',
+ test_method_id='Archive_test',
+ stop_date_range_min=DateTime("2006/06/01"),
+ stop_date_range_max=DateTime("2006/07/01"),
+ )
+ archive.ready()
+ # Create an archive for destination catalog
+ dest = portal_archive.newContent(portal_typ="Archive",
+ catalog_id=self.new_catalog_id,
+ connection_id=self.new_connection_id,
+ deferred_connection_id=self.new_deferred_connection_id,
+ priority=1,
+ test_method_id='Archive_test',
+ stop_date_range_min=DateTime("2006/07/01"),
+ )
+ dest.ready()
+
+ # Do archive
+ portal_archive.manage_archive(destination_archive_id=dest.getId(),
+ archive_id=archive.getId(),
+ update_destination_sql_catalog=True,
+ update_archive_sql_catalog=True,
+ clear_destination_sql_catalog=True,
+ clear_archive_sql_catalog=True)
+
+ get_transaction().commit()
+ self.tic()
+ self.assertEqual(portal_catalog.getSQLCatalog().id, self.new_catalog_id)
+ self.assertEqual(archive.getValidationState(), 'validated')
+ self.assertEqual(dest.getValidationState(), 'validated')
+ # Check objects organisation are indexed
+ # in both archive and current catalog and old one
+ path_list = [self.organisation.getRelativeUrl()]
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.original_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.new_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.archive_connection_id)
+ # Create a new organisation and check it goes in both catalog and not old one
+ self.organisation_1 = module.newContent(portal_type='Organisation',
+ title="GreatTitle3")
+ get_transaction().commit()
+ self.tic()
+ path_list = [self.organisation_1.getRelativeUrl()]
+ self.checkRelativeUrlNotInSQLPathList(path_list, connection_id=self.original_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.new_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.archive_connection_id)
+
+ # Check objects movement are indexed
+ # in archive and old one and not in current catalog
+ path_list = [self.mvt.getRelativeUrl()]
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.original_connection_id)
+ self.checkRelativeUrlNotInSQLPathList(path_list, connection_id=self.new_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.archive_connection_id)
+
+ # Create a new movement and check it goes only in new catalog
+ self.assertEqual(len(self.folder.searchFolder(portal_type="Dummy Movement")), 0)
+ self.assertEquals(100, getInventory(node_uid=self.node.getUid()))
+ self.new_mvt = self._makeMovement(quantity=50, stop_date=DateTime("2006/08/06"),
+ simulation_state='delivered',)
+ get_transaction().commit()
+ self.tic()
+ self.assertEqual(len(self.folder.searchFolder(portal_type="Dummy Movement")), 1)
+ # Check objects movement are indexed
+ # not in archive and old one but in current catalog
+ path_list = [self.new_mvt.getRelativeUrl()]
+ self.checkRelativeUrlNotInSQLPathList(path_list, connection_id=self.original_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.new_connection_id)
+ self.checkRelativeUrlNotInSQLPathList(path_list, connection_id=self.archive_connection_id)
+ self.assertEquals(150, getInventory(node_uid=self.node.getUid()))
+
+ # now play with preference to select to view document from archive
+ portal_preferences = self.getPreferenceTool()
+ self.pref = portal_preferences.newContent(id='user_pref',
+ portal_type='Preference',
+ preferred_archive=archive.getRelativeUrl())
+ get_transaction().commit()
+ self.getPreferenceTool().recursiveReindexObject()
+ self.tic()
+ self.portal.portal_workflow.doActionFor(self.pref,
+ 'enable_action',
+ wf_id='preference_workflow')
+ self.assertEquals(self.pref.getPreferenceState(), 'enabled')
+
+ path_list = [self.pref.getRelativeUrl()]
+ self.checkRelativeUrlNotInSQLPathList(path_list, connection_id=self.original_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.new_connection_id)
+ self.checkRelativeUrlInSQLPathList(path_list, connection_id=self.archive_connection_id)
+
+ self.assertEqual(portal_catalog.getPreferredSQLCatalogId(), archive.getCatalogId())
+ self.assertEqual(len(self.folder.searchFolder(portal_type="Dummy Movement")), 1)
+
+ # As we only have first movement in archive, inventory must be 100
+ self.assertEquals(100, getInventory(node=self.node.getRelativeUrl()))
+
+
+if __name__ == '__main__':
+ framework()
+else:
+ import unittest
+ def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestArchive))
+ return suite
+
Propchange: erp5/trunk/products/ERP5Catalog/tests/testArchive.py
------------------------------------------------------------------------------
svn:executable = *
More information about the Erp5-report
mailing list