<?xml version="1.0"?>
<ZopeData>
  <record id="1" aka="AAAAAAAAAAE=">
    <pickle>
      <tuple>
        <tuple>
          <string>Products.PythonScripts.PythonScript</string>
          <string>PythonScript</string>
        </tuple>
        <none/>
      </tuple>
    </pickle>
    <pickle>
      <dictionary>
        <item>
            <key> <string>Python_magic</string> </key>
            <value>
              <none/>
            </value>
        </item>
        <item>
            <key> <string>Script_magic</string> </key>
            <value> <int>3</int> </value>
        </item>
        <item>
            <key> <string>__ac_local_roles__</string> </key>
            <value>
              <none/>
            </value>
        </item>
        <item>
            <key> <string>_bind_names</string> </key>
            <value>
              <object>
                <klass>
                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
                </klass>
                <tuple/>
                <state>
                  <dictionary>
                    <item>
                        <key> <string>_asgns</string> </key>
                        <value>
                          <dictionary>
                            <item>
                                <key> <string>name_container</string> </key>
                                <value> <string>container</string> </value>
                            </item>
                            <item>
                                <key> <string>name_context</string> </key>
                                <value> <string>context</string> </value>
                            </item>
                            <item>
                                <key> <string>name_m_self</string> </key>
                                <value> <string>script</string> </value>
                            </item>
                            <item>
                                <key> <string>name_subpath</string> </key>
                                <value> <string>traverse_subpath</string> </value>
                            </item>
                          </dictionary>
                        </value>
                    </item>
                  </dictionary>
                </state>
              </object>
            </value>
        </item>
        <item>
            <key> <string>_body</string> </key>
            <value> <string encoding="cdata"><![CDATA[

from Products.ERP5OOo.OOoUtils import OOoParser\n
OOoParser = OOoParser()\n
\n
# Initialise some general variables\n
detailed_report_result = []\n
detailed_report_append = detailed_report_result.append\n
base_category_id_list = []\n
category_path_dict = {}\n
try:\n
  property_id_list = context.portal_classes.getPropertySheetPropertyIdList()\n
except AttributeError:\n
  # Class tool is too old - use an empty list and warn used in detailed report\n
  property_id_list = []\n
  detailed_report_append(\'WARNING: ERP5Type product needs to be upgraded so that portal_classes can supply getPropertySheetPropertyIdList\')\n
\n
def Object_hasRelation(obj):\n
  # Check if there is some related objets.\n
  result = 0\n
  for o in obj.getIndexableChildValueList():\n
    for related in obj.portal_categories.getRelatedValueList(o):\n
      related_url = related.getRelativeUrl()\n
      if related_url.startswith(obj.getRelativeUrl()):\n
        continue\n
      elif related_url.startswith(\'portal_simulation\'):\n
        continue\n
      elif related_url.startswith(\'portal_trash\'):\n
        continue\n
      else:\n
        result = 1\n
        break\n
  return result\n
\n
\n
def getIDFromString(string=None):\n
  """\n
    This function transform a string to a safe and beautiful ID.\n
    It is used here to create a safe category ID from a string.\n
  """\n
  if string is None:\n
    return None\n
  clean_id = \'\'\n
  translation_map = { \'a\'  : [u\'\\xe0\', u\'\\xe3\']\n
                    , \'e\'  : [u\'\\xe9\', u\'\\xe8\']\n
                    , \'i\'  : [u\'\\xed\']\n
                    , \'u\'  : [u\'\\xf9\']\n
                    , \'_\'  : [\' \', \'+\']\n
                    , \'-\'  : [\'-\', u\'\\u2013\']\n
                    , \'and\': [\'&\']\n
                    }\n
  # Replace odd chars by safe ascii\n
  string = string.lower()\n
  string = string.strip()\n
  for (safe_char, char_list) in translation_map.items():\n
    for char in char_list:\n
      string = string.replace(char, safe_char)\n
  # Exclude all non alphanumeric chars\n
  for char in string:\n
    if char.isalnum() or char in translation_map.keys():\n
      clean_id += char\n
  # Delete leading and trailing char which are not alpha-numerics\n
  # This prevent having IDs with starting underscores\n
  while len(clean_id) > 0 and not clean_id[0].isalnum():\n
    clean_id = clean_id[1:]\n
  while len(clean_id) > 0 and not clean_id[-1].isalnum():\n
    clean_id = clean_id[:-1]\n
  return clean_id\n
\n
\n
def isValidID(id):\n
  return id not in property_id_list\n
\n
\n
# Extract tables from the speadsheet file\n
OOoParser.openFile(import_file)\n
filename = OOoParser.getFilename()\n
spreadsheets = OOoParser.getSpreadsheetsMapping()\n
\n
# Some statistics\n
new_category_counter = 0\n
updated_category_counter = 0\n
total_category_counter = 0\n
invalid_category_id_counter = 0\n
deleted_category_counter = 0\n
kept_category_counter = 0\n
\n
for table_name in spreadsheets.keys():\n
\n
  # Get the header of the table\n
  columns_header = spreadsheets[table_name][0]\n
  # Get the mapping to help us know the property according a cell index\n
  property_map = {}\n
  column_index = 0\n
  path_index = 0\n
  for column in columns_header:\n
    column_id = getIDFromString(column)\n
    # This give us the information that the path definition has started\n
    path_def_started = \'path_0\' in property_map.values()\n
    # The path of the category has started to be expressed\n
    if column_id == \'path\':\n
      property_map[column_index] = \'path_\' + str(path_index)\n
      path_index += 1\n
    # The column has no header information\n
    elif column_id in (None, \'\'):\n
      # Are we in the middle of the path definition ?\n
      # If the path definition has started and not ended\n
      if path_def_started and path_index != None:\n
        property_map[column_index] = \'path_\' + str(path_index)\n
        path_index += 1\n
      # else : The path definition is not started or is finished, so ignore the column\n
    # The column has a normal header\n
    else:\n
      # If there is a new column with a header and the path definition has\n
      # started, that seems the path definition has ended\n
      if \'path_0\' in property_map.values():\n
        path_index == None\n
      property_map[column_index] = column_id\n
    column_index += 1\n
\n
  # Construct categories data (with absolute path) from table lines\n
  categories = []\n
  # The first category is the Base category\n
  # 1 table = 1 base category\n
  base_category_name = table_name\n
  base_category_id = getIDFromString(base_category_name)\n
  base_category_id_list.append(base_category_id)\n
  categories.append({ \'path\' : base_category_id\n
                    , \'title\': base_category_name\n
                    })\n
\n
  # This path_elements help us to reconstruct the absolute path\n
  path_elements = []\n
  for line in spreadsheets[table_name][1:]:\n
\n
    # Exclude empty lines\n
    if line.count(\'\') + line.count(None) == len(line):\n
      continue\n
\n
    # Prefetch line datas\n
    cell_index = 0\n
    line_data = {}\n
    for cell in line:\n
      # Get the property corresponding to the cell data\n
      property_id = property_map[cell_index]\n
      line_data[property_id] = cell\n
      cell_index += 1\n
\n
    # Analyse every cells of the line\n
    category_properties = {}\n
    cell_index = 0\n
    for (property_id, cell_data) in line_data.items():\n
\n
      # Try to generate a cell id from cell data\n
      cell_id = getIDFromString(cell_data)\n
      # Returned cell_id can be None or \'\' (empty string). Both have different meaning:\n
      #   None : no data was inputed by the user.\n
      #   \'\'   : data entered by the user, but no good transformation of the string to a safe ID.\n
\n
      # If the cell_id tranformation return an empty string, and if the cell is a path item,\n
      # we should try to use other line data to get a safe id.\n
      if cell_id == \'\' and property_id.startswith(\'path_\'):\n
        for alt_id_source in [\'id\', \'title\']:\n
          if line_data.has_key(alt_id_source):\n
            cell_id = getIDFromString(line_data[alt_id_source])\n
            if cell_id not in (\'\', None):\n
              break\n
\n
      # Ignore empty cells\n
      if cell_id not in (\'\', None):\n
        # Handle normal properties\n
        if not property_id.startswith(\'path_\'):\n
          category_properties[property_id] = cell_data\n
        # Handle \'path\' property\n
        else:\n
          path_element_id = cell_id\n
          # Initialize the list of path elements to the cell element\n
          absolut_path_element_list = [path_element_id,]\n
          # Get the depth of the current element\n
          element_depth = int(property_id[5:])\n
          # Get a path element for each depth level to reach the 0-level\n
          for searched_depth in range(element_depth)[::-1]:\n
            # Get the first path element that correspond to the searched depth\n
            for element in path_elements[::-1]:\n
              if element[\'depth\'] == searched_depth:\n
                # Element found, add it to the list\n
                absolut_path_element_list.append(element[\'value\'])\n
                # Get the next depth\n
                break\n
          category_properties[\'path\'] = \'/\'.join([base_category_id,] + absolut_path_element_list[::-1])\n
\n
          # Save the current raw path item value as title if no title column defined\n
          if \'title\' not in category_properties.keys():\n
            clean_title = cell_data.strip()\n
            # Only set title if it look like a title\n
            # (i.e. its tranformation to ID is not the same as the original value)\n
            if clean_title != cell_id:\n
              category_properties[\'title\'] = clean_title\n
\n
          # Save the path element\n
          path_elements.append({ \'depth\': element_depth\n
                               , \'value\': path_element_id\n
                               })\n
\n
      # Proceed to next cell\n
      cell_index += 1\n
\n
    if len(category_properties) > 0 and \'path\' in category_properties.keys():\n
      categories.append(category_properties)\n
\n
  # Create categories\n
  total_category_counter += len(categories)\n
  for category in categories:\n
    is_new_category = False\n
    keys = category.keys()\n
    if \'path\' in keys:\n
      base_path_obj = context.portal_categories\n
      is_base_category = True\n
      is_valid_category = True\n
      for category_id in category[\'path\'].split(\'/\'):\n
        category_id = str(category_id)\n
        # The current category is not existing\n
        if not base_path_obj.hasContent(category_id):\n
          # Create the category\n
          if is_base_category:\n
            category_type = \'Base Category\'\n
          else:\n
            category_type = \'Category\'\n
          if isValidID(category_id):\n
            base_path_obj.newContent( portal_type       = category_type\n
                                    , id                = category_id\n
                                    , immediate_reindex = 1\n
                                    )\n
            detailed_report_append(\'Created new %s with  URL %s\' % (category_type,\n
                                  base_path_obj[category_id].getRelativeUrl()))\n
          else:\n
            # The ID is invalid, we must break the loop\n
            invalid_category_id_counter += 1\n
            is_valid_category = False\n
            detailed_report_append(\'WARNING: found invalid ID %s\' % category_id)\n
            break\n
          is_new_category = True\n
          new_category_counter += 1\n
        base_path_obj = base_path_obj[category_id]\n
        category_path_dict[base_path_obj.getRelativeUrl()] = None\n
        is_base_category = False\n
      if is_valid_category:\n
        # Only try to update a valid category\n
        new_category = base_path_obj\n
        # Set the category properties\n
        category_update_dict = {}\n
        for key in keys:\n
          key = str(key)\n
          if key not in [\'path\', \'id\']:\n
            value = category[key]\n
            if value is not None:\n
              value = str(value.encode(\'UTF-8\'))\n
            if update_existing_property or is_new_category:\n
              # Always update properties if update_existing_property is set or if this a new category\n
              category_update_dict[key] = value\n
              if not is_new_category:\n
                detailed_report_append(\'Updated %s on Category %s with value %s\' % (key,\n
                                        new_category.getRelativeUrl(), value))\n
            elif value not in (\'\', None) and not new_category.hasProperty(key):\n
              # Only set properties which are not already defined\n
              category_update_dict[key] = value\n
              detailed_report_append(\'Updated %s on Category %s with value %s\' % (key,\n
                                      new_category.getRelativeUrl(), value))\n
        if not is_new_category and category_update_dict:\n
          updated_category_counter += 1\n
        new_category.edit(**category_update_dict)\n
    else:\n
      raise KeyError, \'path was not defined for a category, this should never happen.\'\n
\n
# Find categories to delete\n
category_to_delete_list = []\n
for base_category_id in base_category_id_list:\n
  base_category = context.portal_categories[base_category_id]\n
  for category in base_category.getCategoryChildValueList(is_self_excluded=False):\n
    if not category_path_dict.has_key(category.getRelativeUrl()):\n
      if keep_existing_category or Object_hasRelation(category):\n
        if Object_hasRelation(category):\n
          detailed_report_append(\'WARNING: Category %s is used and can not be deleted\' % category.getRelativeUrl())\n
        else:\n
          detailed_report_append(\'Kept category %s\' % category.getRelativeUrl())\n
        kept_category_counter += 1\n
      else:\n
        detailed_report_append(\'Deleted category %s\' % category.getRelativeUrl())\n
        deleted_category_counter += 1\n
        category_to_delete_list.append(category.getRelativeUrl())\n
\n
# Delete unused categories\n
if not keep_existing_category:\n
  for category in category_to_delete_list:\n
    category = context.portal_categories.resolveCategory(category)\n
    if category is not None:\n
      parent = category.getParentValue()\n
      category_id = category.getId()\n
      parent.deleteContent(category_id)\n
\n
if detailed_report:\n
  # Return a detailed report if requested\n
  # return repr(detailed_report_result)\n
  return \'\\n\'.join(detailed_report_result)\n
\n
# Import is a success, go back to the portal_categories tool\n
return context.REQUEST.RESPONSE.redirect(\n
    # TODO translate\n
  "%s/view?portal_status_message=%s+categories+found+in+%s:+%s+created,+%s+updated,+%s+untouched,+%s+invalid+ID.+%s+existing+categories:+%s+deleted,+%skept."\n
  % ( context.portal_categories.absolute_url()\n
    , total_category_counter\n
    , filename\n
    , new_category_counter\n
    , updated_category_counter\n
    , total_category_counter - new_category_counter - updated_category_counter\n
    , invalid_category_id_counter\n
    , deleted_category_counter + kept_category_counter\n
    , deleted_category_counter\n
    , kept_category_counter\n
    )\n
)\n


]]></string> </value>
        </item>
        <item>
            <key> <string>_code</string> </key>
            <value>
              <none/>
            </value>
        </item>
        <item>
            <key> <string>_filepath</string> </key>
            <value>
              <none/>
            </value>
        </item>
        <item>
            <key> <string>_owner</string> </key>
            <value>
              <none/>
            </value>
        </item>
        <item>
            <key> <string>_params</string> </key>
            <value> <string>import_file, update_existing_property=False, keep_existing_category=True, detailed_report=False, **kw</string> </value>
        </item>
        <item>
            <key> <string>errors</string> </key>
            <value>
              <tuple/>
            </value>
        </item>
        <item>
            <key> <string>func_code</string> </key>
            <value>
              <object>
                <klass>
                  <global name="FuncCode" module="Shared.DC.Scripts.Signature"/>
                </klass>
                <tuple/>
                <state>
                  <dictionary>
                    <item>
                        <key> <string>co_argcount</string> </key>
                        <value> <int>4</int> </value>
                    </item>
                    <item>
                        <key> <string>co_varnames</string> </key>
                        <value>
                          <tuple>
                            <string>import_file</string>
                            <string>update_existing_property</string>
                            <string>keep_existing_category</string>
                            <string>detailed_report</string>
                            <string>kw</string>
                            <string>Products.ERP5OOo.OOoUtils</string>
                            <string>OOoParser</string>
                            <string>detailed_report_result</string>
                            <string>_getattr_</string>
                            <string>detailed_report_append</string>
                            <string>base_category_id_list</string>
                            <string>category_path_dict</string>
                            <string>context</string>
                            <string>property_id_list</string>
                            <string>AttributeError</string>
                            <string>Object_hasRelation</string>
                            <string>None</string>
                            <string>getIDFromString</string>
                            <string>isValidID</string>
                            <string>filename</string>
                            <string>spreadsheets</string>
                            <string>new_category_counter</string>
                            <string>updated_category_counter</string>
                            <string>total_category_counter</string>
                            <string>invalid_category_id_counter</string>
                            <string>deleted_category_counter</string>
                            <string>kept_category_counter</string>
                            <string>_getiter_</string>
                            <string>table_name</string>
                            <string>_getitem_</string>
                            <string>columns_header</string>
                            <string>property_map</string>
                            <string>column_index</string>
                            <string>path_index</string>
                            <string>column</string>
                            <string>column_id</string>
                            <string>path_def_started</string>
                            <string>str</string>
                            <string>_write_</string>
                            <string>categories</string>
                            <string>base_category_name</string>
                            <string>base_category_id</string>
                            <string>path_elements</string>
                            <string>line</string>
                            <string>len</string>
                            <string>cell_index</string>
                            <string>line_data</string>
                            <string>cell</string>
                            <string>property_id</string>
                            <string>category_properties</string>
                            <string>cell_data</string>
                            <string>cell_id</string>
                            <string>alt_id_source</string>
                            <string>path_element_id</string>
                            <string>absolut_path_element_list</string>
                            <string>int</string>
                            <string>element_depth</string>
                            <string>range</string>
                            <string>searched_depth</string>
                            <string>element</string>
                            <string>clean_title</string>
                            <string>category</string>
                            <string>False</string>
                            <string>is_new_category</string>
                            <string>keys</string>
                            <string>base_path_obj</string>
                            <string>True</string>
                            <string>is_base_category</string>
                            <string>is_valid_category</string>
                            <string>category_id</string>
                            <string>category_type</string>
                            <string>new_category</string>
                            <string>category_update_dict</string>
                            <string>key</string>
                            <string>value</string>
                            <string>_apply_</string>
                            <string>KeyError</string>
                            <string>category_to_delete_list</string>
                            <string>base_category</string>
                            <string>parent</string>
                          </tuple>
                        </value>
                    </item>
                  </dictionary>
                </state>
              </object>
            </value>
        </item>
        <item>
            <key> <string>func_defaults</string> </key>
            <value>
              <tuple>
                <int>0</int>
                <int>1</int>
                <int>0</int>
              </tuple>
            </value>
        </item>
        <item>
            <key> <string>id</string> </key>
            <value> <string>CategoryTool_importCategoryFile</string> </value>
        </item>
        <item>
            <key> <string>warnings</string> </key>
            <value>
              <tuple/>
            </value>
        </item>
      </dictionary>
    </pickle>
  </record>
</ZopeData>
