[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