Exemple #1
0
 def _adaptWFDataForItemsAndMeetings(self):
     """We use PM default WFs, no more meetingcommunes(item)_workflow...
        Adapt:
        - workflow_history for items and meetings;
        - takenOverByInfos for items."""
     logger.info('Updating WF history items and meetings to use new WF id...')
     wfTool = api.portal.get_tool('portal_workflow')
     catalog = api.portal.get_tool('portal_catalog')
     for cfg in self.tool.objectValues('MeetingConfig'):
         # this will call especially part where we duplicate WF and apply WFAdaptations
         cfg.registerPortalTypes()
         itemWFId = cfg.getItemWorkflow()
         for brain in catalog(portal_type=(cfg.getItemTypeName(), cfg.getMeetingTypeName())):
             itemOrMeeting = brain.getObject()
             itemOrMeetingWFId = wfTool.getWorkflowsFor(itemOrMeeting)[0].getId()
             if itemOrMeetingWFId not in itemOrMeeting.workflow_history:
                 wf_history_key = self._get_wh_key(itemOrMeeting)
                 itemOrMeeting.workflow_history[itemOrMeetingWFId] = \
                     tuple(itemOrMeeting.workflow_history[wf_history_key])
                 del itemOrMeeting.workflow_history[wf_history_key]
                 # do this so change is persisted
                 itemOrMeeting.workflow_history = itemOrMeeting.workflow_history
             else:
                 # already migrated
                 break
             if itemOrMeeting.__class__.__name__ == 'MeetingItem':
                 takenOverByInfos = itemOrMeeting.takenOverByInfos.copy()
                 newTakenOverByInfos = PersistentMapping()
                 for k, v in takenOverByInfos.items():
                     wf_name, state = k.split('__wfstate__')
                     newTakenOverByInfos['{0}__wfstate__{1}'.format(itemWFId, state)] = v
                 if sorted(newTakenOverByInfos.keys()) != sorted(takenOverByInfos.keys()):
                     itemOrMeeting.takenOverByInfos = newTakenOverByInfos
     logger.info('Done.')
 def test_persistent_mapping(self):
     value = PersistentMapping({'foo': 'bar'})
     self.assertEquals({u'foo': u'bar'}, json_compatible(value))
     self.assertEquals('{"foo": "bar"}', json.dumps(json_compatible(value)))
     self.assertIsInstance(json_compatible(value.keys()[0]), unicode,
                           'Dict keys should be converted recursively.')
     self.assertIsInstance(json_compatible(value.values()[0]), unicode,
                           'Dict values should be converted recursively.')
 def test_persistent_mapping(self):
     value = PersistentMapping({'foo': 'bar'})
     self.assertEquals({u'foo': u'bar'}, json_compatible(value))
     self.assertEquals('{"foo": "bar"}', json.dumps(json_compatible(value)))
     self.assertIsInstance(json_compatible(value.keys()[0]),
                           unicode,
                           'Dict keys should be converted recursively.')
     self.assertIsInstance(json_compatible(value.values()[0]),
                           unicode,
                           'Dict values should be converted recursively.')
def upgrade_carousel_tiles_custom_url(context):
    # Get covers
    covers = context.portal_catalog(portal_type='collective.cover.content')
    logger.info('About to update %s covers' % len(covers))
    for cover in covers:
        obj = cover.getObject()
        tile_ids = obj.list_tiles(types=[
            u'collective.cover.carousel',
            u'collective.cover.list'
        ])
        for tile_id in tile_ids:
            tile = obj.get_tile(tile_id)
            old_data = ITileDataManager(tile).get()
            uuids = old_data['uuids']
            if isinstance(uuids, PersistentMapping):
                # This tile is fixed, carry on
                logger.info(
                    'Tile %s at %s was already updated' %
                    (tile_id, cover.getPath())
                )
                continue
            if not uuids:
                # This tile did not have data, so ignore
                logger.info(
                    'Tile %s at %s did not have any data' %
                    (tile_id, cover.getPath())
                )
                continue

            new_data = PersistentMapping()
            order = 0
            for uuid in uuids:
                if uuid not in new_data.keys():
                    entry = PersistentMapping()
                    entry[u'order'] = unicode(order)
                    new_data[uuid] = entry
                    order += 1

            old_data['uuids'] = new_data
            ITileDataManager(tile).set(old_data)

            logger.info(
                'Tile %s at %s updated' % (tile_id, cover.getPath())
            )
    logger.info('Done')
Exemple #5
0
    def checkBasicOps(self):
        from persistent.mapping import PersistentMapping
        m = PersistentMapping({'x': 1}, a=2, b=3)
        m['name'] = 'bob'
        self.assertEqual(m['name'], "bob")
        self.assertEqual(m.get('name', 42), "bob")
        self.assert_('name' in m)

        try:
            m['fred']
        except KeyError:
            pass
        else:
            self.fail("expected KeyError")
        self.assert_('fred' not in m)
        self.assertEqual(m.get('fred'), None)
        self.assertEqual(m.get('fred', 42), 42)

        keys = m.keys()
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = m.values()
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = m.items()
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])

        keys = list(m.iterkeys())
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = list(m.itervalues())
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = list(m.iteritems())
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])
    def checkBasicOps(self):
        from persistent.mapping import PersistentMapping
        m = PersistentMapping({'x': 1}, a=2, b=3)
        m['name'] = 'bob'
        self.assertEqual(m['name'], "bob")
        self.assertEqual(m.get('name', 42), "bob")
        self.assert_('name' in m)

        try:
            m['fred']
        except KeyError:
            pass
        else:
            self.fail("expected KeyError")
        self.assert_('fred' not in m)
        self.assertEqual(m.get('fred'), None)
        self.assertEqual(m.get('fred', 42), 42)

        keys = m.keys()
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = m.values()
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = m.items()
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])

        keys = list(m.iterkeys())
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = list(m.itervalues())
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = list(m.iteritems())
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])
    def checkBasicOps(self):
        from persistent.mapping import PersistentMapping

        m = PersistentMapping({"x": 1}, a=2, b=3)
        m["name"] = "bob"
        self.assertEqual(m["name"], "bob")
        self.assertEqual(m.get("name", 42), "bob")
        self.assert_("name" in m)

        try:
            m["fred"]
        except KeyError:
            pass
        else:
            self.fail("expected KeyError")
        self.assert_("fred" not in m)
        self.assertEqual(m.get("fred"), None)
        self.assertEqual(m.get("fred", 42), 42)

        keys = m.keys()
        keys.sort()
        self.assertEqual(keys, ["a", "b", "name", "x"])

        values = m.values()
        values.sort()
        self.assertEqual(values, [1, 2, 3, "bob"])

        items = m.items()
        items.sort()
        self.assertEqual(items, [("a", 2), ("b", 3), ("name", "bob"), ("x", 1)])

        keys = list(m.iterkeys())
        keys.sort()
        self.assertEqual(keys, ["a", "b", "name", "x"])

        values = list(m.itervalues())
        values.sort()
        self.assertEqual(values, [1, 2, 3, "bob"])

        items = list(m.iteritems())
        items.sort()
        self.assertEqual(items, [("a", 2), ("b", 3), ("name", "bob"), ("x", 1)])
class BibliographyTool(UniqueObject, Folder, ## ActionProviderBase,
                       DuplicatesCriteriaManager):

    """Tool for managing import and export functionality
       as well as some resources of the BibliographyFolders
       and -Entries.
    """

    implements(IBibliographyTool)

    __allow_access_to_unprotected_subobjects__ = 1

    id = 'portal_bibliography'
    meta_type = 'Bibliography Tool'
    show_isbn_link = 0
    allow_folder_intro = 0
    bibfolders_translatable = 1
    bibrefitems_translatable = 1
    support_member_references = False
    default_idcooker_id = 'etal'
    use_pids_on_import = True
    cook_ids_on_bibref_creation = False
    cook_ids_after_bibref_edit = False
    synchronize_pdffile_attributes = False
    enable_duplicates_manager = True
    member_types = []
    sort_members_on = ''
    select_members_attr = ''
    members_search_on_attr = ''
    infer_author_references_after_edit = True
    infer_author_references_after_import = True
    authorof_implies_owner = False
    authorof_implies_creator = False
    allow_pdfupload_portal_policy = True
    allow_pdfupload_for_types = REFERENCE_TYPES
    searchable_bibfolders = True
    preprint_servers = []
    allow_additional_fields = False
    additional_fields = ['howpublished','recommendedby']  # just an example

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    manage_options = (
        (Folder.manage_options[0],)
        + DuplicatesCriteriaManager.manage_options
        + Folder.manage_options[2:]
        )

    _properties = Folder._properties + (
        {'id':'default_idcooker_id',
         'type':'selection',
         'select_variable':'listIdCookers',
         'mode':'w',
         },
        {'id':'cook_ids_on_bibref_creation',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'cook_ids_after_bibref_edit',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'use_pids_on_import',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'synchronize_pdffile_attributes',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'enable_duplicates_manager',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'allow_folder_intro',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'allow_pdfupload_portal_policy',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'allow_pdfupload_for_types',
         'type':'multiple selection',
         'select_variable':'getReferenceTypes',
         'mode':'w',
         },
        {'id':'bibfolders_translatable',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'bibrefitems_translatable',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'support_member_references',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'member_types',
         'type':'multiple selection',
         'select_variable':'getPortalTypeNames',
         'mode':'w',
         },
        {'id':'sort_members_on',
         'type':'selection',
         'select_variable':'getFieldIndexes',
         'mode':'w',
         },
        {'id':'select_members_attr',
         'type':'selection',
         'select_variable':'getMetaDataColumns',
         'mode':'w',
         },
        {'id':'members_search_on_attr',
         'type':'selection',
         'select_variable':'getTextIndexes',
         'mode':'w',
         },
        {'id':'infer_author_references_after_edit',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'infer_author_references_after_import',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'authorof_implies_owner',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'authorof_implies_creator',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'show_isbn_link',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'preprint_servers',
         'type':'lines',
         'mode':'w',
         },
        {'id':'searchable_bibfolders',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'allow_additional_fields',
         'type':'boolean',
         'mode':'w',
         },
        {'id':'additional_fields',
         'type':'lines',
         'mode':'w',
         },
        )
    _match_criteria = {}
    _default_criteria = ('bibliography type',)

    def __init__(self):
        self._setObject('IdCookers', IdCookerFolder('IdCookers', ''))
        DuplicatesCriteriaManager.__init__(self)
        # Add the local reference types registry
        self._reference_types = PersistentMapping()
        # Populate it initially with those types declared in config.py
        for ref_type in REFERENCE_TYPES:
            self._reference_types[ref_type] = None

    def isBibFolderTranslatable(self):
        """ return what is in bibfolders_translatable property
            this is only half of the truth!!! (ITranslatable)
        """
        return self.bibfolders_translatable

    def isBibrefItemTranslatable(self):
        """ return what is in bibrefitems_translatable property
            this is only half of the truth!!! (ITranslatable)
        """
        return self.bibrefitems_translatable

    security.declarePublic('getMemberTypes')
    def getMemberTypes(self, default=()):
        member_types = self.getProperty('member_types', default)
        if not isinstance(member_types, (list, tuple)):
            return (member_types,)
        else:
            return member_types

    security.declarePublic('getSortMembersOn')
    def getSortMembersOn(self, default='getId'):
        return self.getProperty('sort_members_on', default)

    security.declarePublic('getSelectMembersAttr')
    def getSelectMembersAttr(self, default='Title'):
        return self.getProperty('select_members_attr', default)

    security.declarePublic('getMembersSearchOnAttr')
    def getMembersSearchOnAttr(self, default='Title'):
        return self.getProperty('members_search_on_attr', default)

    security.declarePublic('getReferenceTypes')
    def getReferenceTypes(self, display=False):
        """
        returns a list with the names (meta types) of the
        currently registered reference types of a BibliographyFolder
        """
        if display:
            return DisplayList(tuple([ (key, key) for key in self.getReferenceTypes() ]))
        else:
            return self._reference_types.keys()

    security.declarePublic('getBibFolderTypes')
    def getBibFolderTypes(self):
        """
        returns a list with the names (meta types) of the
        currently registered bibfolder types
        """
        return BIBFOLDER_TYPES

    security.declareProtected(permissions.ManageReferenceTypes,
                              'registerReferenceType')
    def registerReferenceType(self, portal_type):
        """Add portal_type to the list that should be considered references
        for this plone instance.  Do not fail on duplicates.
        """
        # self._reference_types is a PersistentMapping
        self._reference_types[portal_type] = None

    security.declareProtected(permissions.ManageReferenceTypes,
                              'unregisterReferenceType')
    def unregisterReferenceType(self, portal_type):
        """Remove portal_type from the list that should be considered
        references for this plone instance.  Do not fail if the type is not
        already registered.
        """
        # self._reference_types is a PersistentMapping
        try:
            del self._reference_types[portal_type]
        except KeyError:
            pass

    security.declarePublic('getImportFormatNames')
    def getImportFormatNames(self, with_unavailables=False, with_disabled=False):
        """
        returns a list with the names of the supported import formats
        """
        parsers = component.getAllUtilitiesRegisteredFor(IBibliographyParser)
        return [parser.getFormatName() \
                for parser in parsers if (parser.isAvailable() or with_unavailables) and (parser.isEnabled() or with_disabled) ]

    security.declarePublic('getImportFormatExtensions')
    def getImportFormatExtensions(self, with_unavailables=False, with_disabled=False):
        """
        returns a list with the file name extensions
        of the supported import formats
        """
        parsers = component.getAllUtilitiesRegisteredFor(IBibliographyParser)
        return [parser.getFormatExtension() \
                for parser in parsers if (parser.isAvailable() or with_unavailables) and (self.isParserEnabled(parser.getFormatName()) or with_disabled)]

    def getImportFormatDescriptions(self, with_unavailables=False, with_disabled=False):
        """
        returns a list with the description texts
        of the supported import formats
        """
        parsers = component.getAllUtilitiesRegisteredFor(IBibliographyParser)
        return [parser.Description() \
                for parser in parsers if (parser.isAvailable() or with_unavailables) and (parser.isEnabled() or with_disabled) ]

    security.declarePublic('getExportFormatNames')
    def getExportFormatNames(self, with_unavailables=False, with_disabled=False):
        """
        returns a list with the names of the supported export formats
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        return [ renderer.__name__ for renderer in utils
                 if (renderer.available or with_unavailables) and
                    (self.isRendererEnabled(renderer.__name__) or with_disabled) ]

    security.declarePublic('getExportFormatExtensions')
    def getExportFormatExtensions(self, with_unavailables=False, with_disabled=False):
        """
        returns a list with the file name extensions
        of the supported export formats
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        return [ renderer.target_format for renderer in utils
                 if (renderer.available or with_unavailables) and
                    (self.isRendererEnabled(renderer.__name__) or with_disabled) ]

    def getExportFormatDescriptions(self, with_unavailables=False, with_disabled=False):
        """
        returns a list with the description texts
        of the supported export formats
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        return [ renderer.description for renderer in utils
                 if (renderer.available or with_unavailables) and
                    (self.isRendererEnabled(renderer.__name__) or with_disabled) ]

    security.declarePublic('getExportFormats')
    def getExportFormats(self, with_unavailables=False, with_disabled=False):
        """
        returns a list of (name, extension, description) tuples
        of the supported export formats
        """
        export_formats = zip(
            self.getExportFormatNames(with_unavailables=with_unavailables,
                                      with_disabled=with_disabled),
            self.getExportFormatExtensions(with_unavailables=with_unavailables,
                                           with_disabled=with_disabled),
            self.getExportFormatDescriptions(with_unavailables=with_unavailables,
                                            with_disabled=with_disabled))
        export_formats.sort()
        return export_formats

    security.declarePublic('getImportFormats')
    def getImportFormats(self, with_unavailables=False, with_disabled=False):
        """
        returns a list of (name, extension, description) tuples
        of the supported import formats
        """
        supported_parsers = zip(self.getImportFormatNames(with_unavailables=with_unavailables, with_disabled=with_disabled),
                                self.getImportFormatExtensions(with_unavailables=with_unavailables, with_disabled=with_disabled),
                                self.getImportFormatDescriptions(with_unavailables=with_unavailables, with_disabled=with_disabled))
        supported_parsers.sort()
        return supported_parsers

    security.declareProtected(View, 'render')
    def render(self, entry, format='', output_encoding=None, **kw):
        """
        renders a BibliographyEntry object in the specified format
        """
        renderer = self.getRenderer(format=format, **kw)
        if renderer:
            return renderer.render(entry, output_encoding=output_encoding, **kw)
        else:
            return None

    security.declareProtected(View, 'isParserEnabled')
    def isParserEnabled(self, name):
        """ Check cmfbibat propertysheet """
        return self.getSheetProperty(name, 'parser_enabled')

    security.declareProtected(View, 'isRendererEnabled')
    def isRendererEnabled(self, name):
        """ Check cmfbibat propertysheet """
        return self.getSheetProperty(name, 'renderer_enabled')

    security.declareProtected(View, 'getRenderer')
    def getRenderer(self, format, with_unavailables=False, with_disabled=False, **kw):
        """
        returns the renderer for the specified format
        first looks for a renderer with the 'format' name
        next looks for a renderer with the 'format' extension
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        for renderer in utils:
            if (renderer.available or with_unavailables) and \
               (renderer.enabled or with_disabled):
                if format.lower() == renderer.__name__.lower():
                    return renderer.__of__(self)
                if format.lower() == renderer.target_format.lower():
                    return renderer.__of__(self)
        return None


    security.declareProtected(View, 'getEntries')
    def getEntries(self, source, format, file_name=None, input_encoding='utf-8'):
        """
        main routine to be called from BibliographyFolders
        returns a list with the parsed entries
        """

        source = self.checkEncoding(source, input_encoding)
        format = self.checkFormat(source, format, file_name)
        parser = self.getParser(format)

        if parser:
            try:
                return parser.getEntries(source)
            except Exception, e:
                LOG.error('Import error while importing file (%s)' % e, exc_info=True)
                raise RuntimeError('An error occured (%s) - please check the log file for details' % e)
        else:
class BibliographyTool(
        UniqueObject,
        Folder,  ## ActionProviderBase,
        DuplicatesCriteriaManager):
    """Tool for managing import and export functionality
       as well as some resources of the BibliographyFolders
       and -Entries.
    """

    implements(IBibliographyTool)

    __allow_access_to_unprotected_subobjects__ = 1

    id = 'portal_bibliography'
    meta_type = 'Bibliography Tool'
    show_isbn_link = 0
    allow_folder_intro = 0
    bibfolders_translatable = 1
    bibrefitems_translatable = 1
    support_member_references = False
    default_idcooker_id = 'etal'
    use_pids_on_import = True
    cook_ids_on_bibref_creation = False
    cook_ids_after_bibref_edit = False
    synchronize_pdffile_attributes = False
    enable_duplicates_manager = True
    member_types = []
    sort_members_on = ''
    select_members_attr = ''
    members_search_on_attr = ''
    infer_author_references_after_edit = True
    infer_author_references_after_import = True
    authorof_implies_owner = False
    authorof_implies_creator = False
    allow_pdfupload_portal_policy = True
    allow_pdfupload_for_types = REFERENCE_TYPES
    searchable_bibfolders = True
    preprint_servers = []
    allow_additional_fields = False
    additional_fields = ['howpublished', 'recommendedby']  # just an example

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    manage_options = ((Folder.manage_options[0], ) +
                      DuplicatesCriteriaManager.manage_options +
                      Folder.manage_options[2:])

    _properties = Folder._properties + (
        {
            'id': 'default_idcooker_id',
            'type': 'selection',
            'select_variable': 'listIdCookers',
            'mode': 'w',
        },
        {
            'id': 'cook_ids_on_bibref_creation',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'cook_ids_after_bibref_edit',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'use_pids_on_import',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'synchronize_pdffile_attributes',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'enable_duplicates_manager',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'allow_folder_intro',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'allow_pdfupload_portal_policy',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'allow_pdfupload_for_types',
            'type': 'multiple selection',
            'select_variable': 'getReferenceTypes',
            'mode': 'w',
        },
        {
            'id': 'bibfolders_translatable',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'bibrefitems_translatable',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'support_member_references',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'member_types',
            'type': 'multiple selection',
            'select_variable': 'getPortalTypeNames',
            'mode': 'w',
        },
        {
            'id': 'sort_members_on',
            'type': 'selection',
            'select_variable': 'getFieldIndexes',
            'mode': 'w',
        },
        {
            'id': 'select_members_attr',
            'type': 'selection',
            'select_variable': 'getMetaDataColumns',
            'mode': 'w',
        },
        {
            'id': 'members_search_on_attr',
            'type': 'selection',
            'select_variable': 'getTextIndexes',
            'mode': 'w',
        },
        {
            'id': 'infer_author_references_after_edit',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'infer_author_references_after_import',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'authorof_implies_owner',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'authorof_implies_creator',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'show_isbn_link',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'preprint_servers',
            'type': 'lines',
            'mode': 'w',
        },
        {
            'id': 'searchable_bibfolders',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'allow_additional_fields',
            'type': 'boolean',
            'mode': 'w',
        },
        {
            'id': 'additional_fields',
            'type': 'lines',
            'mode': 'w',
        },
    )
    _match_criteria = {}
    _default_criteria = ('bibliography type', )

    def __init__(self):
        self._setObject('IdCookers', IdCookerFolder('IdCookers', ''))
        DuplicatesCriteriaManager.__init__(self)
        # Add the local reference types registry
        self._reference_types = PersistentMapping()
        # Populate it initially with those types declared in config.py
        for ref_type in REFERENCE_TYPES:
            self._reference_types[ref_type] = None

    def isBibFolderTranslatable(self):
        """ return what is in bibfolders_translatable property
            this is only half of the truth!!! (ITranslatable)
        """
        return self.bibfolders_translatable

    def isBibrefItemTranslatable(self):
        """ return what is in bibrefitems_translatable property
            this is only half of the truth!!! (ITranslatable)
        """
        return self.bibrefitems_translatable

    security.declarePublic('getMemberTypes')

    def getMemberTypes(self, default=()):
        member_types = self.getProperty('member_types', default)
        if not isinstance(member_types, (list, tuple)):
            return (member_types, )
        else:
            return member_types

    security.declarePublic('getSortMembersOn')

    def getSortMembersOn(self, default='getId'):
        return self.getProperty('sort_members_on', default)

    security.declarePublic('getSelectMembersAttr')

    def getSelectMembersAttr(self, default='Title'):
        return self.getProperty('select_members_attr', default)

    security.declarePublic('getMembersSearchOnAttr')

    def getMembersSearchOnAttr(self, default='Title'):
        return self.getProperty('members_search_on_attr', default)

    security.declarePublic('getReferenceTypes')

    def getReferenceTypes(self, display=False):
        """
        returns a list with the names (meta types) of the
        currently registered reference types of a BibliographyFolder
        """
        if display:
            return DisplayList(
                tuple([(key, key) for key in self.getReferenceTypes()]))
        else:
            return self._reference_types.keys()

    security.declarePublic('getBibFolderTypes')

    def getBibFolderTypes(self):
        """
        returns a list with the names (meta types) of the
        currently registered bibfolder types
        """
        return BIBFOLDER_TYPES

    security.declareProtected(permissions.ManageReferenceTypes,
                              'registerReferenceType')

    def registerReferenceType(self, portal_type):
        """Add portal_type to the list that should be considered references
        for this plone instance.  Do not fail on duplicates.
        """
        # self._reference_types is a PersistentMapping
        self._reference_types[portal_type] = None

    security.declareProtected(permissions.ManageReferenceTypes,
                              'unregisterReferenceType')

    def unregisterReferenceType(self, portal_type):
        """Remove portal_type from the list that should be considered
        references for this plone instance.  Do not fail if the type is not
        already registered.
        """
        # self._reference_types is a PersistentMapping
        try:
            del self._reference_types[portal_type]
        except KeyError:
            pass

    security.declarePublic('getImportFormatNames')

    def getImportFormatNames(self,
                             with_unavailables=False,
                             with_disabled=False):
        """
        returns a list with the names of the supported import formats
        """
        parsers = component.getAllUtilitiesRegisteredFor(IBibliographyParser)
        return [parser.getFormatName() \
                for parser in parsers if (parser.isAvailable() or with_unavailables) and (parser.isEnabled() or with_disabled) ]

    security.declarePublic('getImportFormatExtensions')

    def getImportFormatExtensions(self,
                                  with_unavailables=False,
                                  with_disabled=False):
        """
        returns a list with the file name extensions
        of the supported import formats
        """
        parsers = component.getAllUtilitiesRegisteredFor(IBibliographyParser)
        return [parser.getFormatExtension() \
                for parser in parsers if (parser.isAvailable() or with_unavailables) and (self.isParserEnabled(parser.getFormatName()) or with_disabled)]

    def getImportFormatDescriptions(self,
                                    with_unavailables=False,
                                    with_disabled=False):
        """
        returns a list with the description texts
        of the supported import formats
        """
        parsers = component.getAllUtilitiesRegisteredFor(IBibliographyParser)
        return [parser.Description() \
                for parser in parsers if (parser.isAvailable() or with_unavailables) and (parser.isEnabled() or with_disabled) ]

    security.declarePublic('getExportFormatNames')

    def getExportFormatNames(self,
                             with_unavailables=False,
                             with_disabled=False):
        """
        returns a list with the names of the supported export formats
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        return [
            renderer.__name__ for renderer in utils
            if (renderer.available or with_unavailables) and (
                self.isRendererEnabled(renderer.__name__) or with_disabled)
        ]

    security.declarePublic('getExportFormatExtensions')

    def getExportFormatExtensions(self,
                                  with_unavailables=False,
                                  with_disabled=False):
        """
        returns a list with the file name extensions
        of the supported export formats
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        return [
            renderer.target_format for renderer in utils
            if (renderer.available or with_unavailables) and (
                self.isRendererEnabled(renderer.__name__) or with_disabled)
        ]

    def getExportFormatDescriptions(self,
                                    with_unavailables=False,
                                    with_disabled=False):
        """
        returns a list with the description texts
        of the supported export formats
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        return [
            renderer.description for renderer in utils
            if (renderer.available or with_unavailables) and (
                self.isRendererEnabled(renderer.__name__) or with_disabled)
        ]

    security.declarePublic('getExportFormats')

    def getExportFormats(self, with_unavailables=False, with_disabled=False):
        """
        returns a list of (name, extension, description) tuples
        of the supported export formats
        """
        export_formats = zip(
            self.getExportFormatNames(with_unavailables=with_unavailables,
                                      with_disabled=with_disabled),
            self.getExportFormatExtensions(with_unavailables=with_unavailables,
                                           with_disabled=with_disabled),
            self.getExportFormatDescriptions(
                with_unavailables=with_unavailables,
                with_disabled=with_disabled))
        export_formats.sort()
        return export_formats

    security.declarePublic('getImportFormats')

    def getImportFormats(self, with_unavailables=False, with_disabled=False):
        """
        returns a list of (name, extension, description) tuples
        of the supported import formats
        """
        supported_parsers = zip(
            self.getImportFormatNames(with_unavailables=with_unavailables,
                                      with_disabled=with_disabled),
            self.getImportFormatExtensions(with_unavailables=with_unavailables,
                                           with_disabled=with_disabled),
            self.getImportFormatDescriptions(
                with_unavailables=with_unavailables,
                with_disabled=with_disabled))
        supported_parsers.sort()
        return supported_parsers

    security.declareProtected(View, 'render')

    def render(self, entry, format='', output_encoding=None, **kw):
        """
        renders a BibliographyEntry object in the specified format
        """
        renderer = self.getRenderer(format=format, **kw)
        if renderer:
            return renderer.render(entry,
                                   output_encoding=output_encoding,
                                   **kw)
        else:
            return None

    security.declareProtected(View, 'isParserEnabled')

    def isParserEnabled(self, name):
        """ Check cmfbibat propertysheet """
        return self.getSheetProperty(name, 'parser_enabled')

    security.declareProtected(View, 'isRendererEnabled')

    def isRendererEnabled(self, name):
        """ Check cmfbibat propertysheet """
        return self.getSheetProperty(name, 'renderer_enabled')

    security.declareProtected(View, 'getRenderer')

    def getRenderer(self,
                    format,
                    with_unavailables=False,
                    with_disabled=False,
                    **kw):
        """
        returns the renderer for the specified format
        first looks for a renderer with the 'format' name
        next looks for a renderer with the 'format' extension
        """
        utils = component.getAllUtilitiesRegisteredFor(IBibliographyRenderer)
        for renderer in utils:
            if (renderer.available or with_unavailables) and \
               (renderer.enabled or with_disabled):
                if format.lower() == renderer.__name__.lower():
                    return renderer.__of__(self)
                if format.lower() == renderer.target_format.lower():
                    return renderer.__of__(self)
        return None

    security.declareProtected(View, 'getEntries')

    def getEntries(self,
                   source,
                   format,
                   file_name=None,
                   input_encoding='utf-8'):
        """
        main routine to be called from BibliographyFolders
        returns a list with the parsed entries
        """

        source = self.checkEncoding(source, input_encoding)
        format = self.checkFormat(source, format, file_name)
        parser = self.getParser(format)

        if parser:
            try:
                return parser.getEntries(source)
            except Exception, e:
                LOG.error('Import error while importing file (%s)' % e,
                          exc_info=True)
                raise RuntimeError(
                    'An error occured (%s) - please check the log file for details'
                    % e)
        else:
class Box(Persistent):
    implements(IDepositBox)

    def __init__(self, max_age=config.MAX_AGE, purge_days=config.PURGE_DAYS):
        self.data = PersistentMapping()
        self._last_purge = int(time.time())
        self.max_age = max_age
        self.purge_days = purge_days

    def _generate_new_id(self):
        """Generate new id.
        """
        new_id = id_generator()
        while new_id in self.data.keys():
            new_id = id_generator()
        return new_id

    def put(self, value, token=None):
        """Put value in box, with optional token, and return generated id.

        Calling this method also does a purge once a day (well, when
        the last purge was at least 24 hours ago).  The frequency can
        be controlled with the purge_days attribute.
        """
        cutoff = int(time.time()) - (self.purge_days * 86400)
        if self._last_purge < cutoff:
            self.purge()

        if value is None:
            raise ValueError
        id = self._generate_new_id()
        self.data[id] = BoxItem(token, value, confirmed=False)
        return id

    def edit(self, secret, value, token=None):
        """Edit value in the box, when secret and optional token match.
        """
        if value is None:
            raise ValueError
        stored = self.get(secret, token=token)
        if value == stored:
            # No change
            return
        self.data[secret] = BoxItem(token, value, confirmed=True)

    def get(self, secret, token=None):
        stored = self.data.get(secret)
        if stored is None:
            return None
        if stored.token != token:
            # raise Exception
            return None
        if not stored.confirmed:
            # Purge this item when it is expired:
            cutoff = int(time.time()) - self.max_age * 86400
            if stored.timestamp < cutoff:
                del self.data[secret]
                return None
            if token:
                # When there is a token, the item must be confirmed
                # before we return the value.  Main use case: email
                # confirmation.
                return None
        return stored.value

    def confirm(self, secret, token=None):
        """Confirm the item/token and return whether this succeeded or not.
        """
        stored = self.data.get(secret)
        if stored is None:
            return None
        if stored.token != token:
            # raise Exception?
            return None
        if not stored.confirmed:
            # First check if the confirmation comes too late.
            cutoff = int(time.time()) - self.max_age * 86400
            if stored.timestamp < cutoff:
                del self.data[secret]
                # Report back that we have failed, in case anyone
                # wants to know.
                return False
        stored.confirmed = True
        return True

    def pop(self, secret, token=None):
        stored = self.get(secret, token=token)
        if stored is None:
            return None
        self.data.pop(secret)
        return stored

    def get_all_confirmed(self):
        for key, stored in self.data.items():
            if stored.confirmed:
                yield stored.value

    def purge(self):
        """Purge items that have expired.

        Confirmed items are not purged.
        """
        cutoff = int(time.time()) - self.max_age * 86400
        logger.info("Started purging data.")
        for key, stored in self.data.items():
            if not stored.confirmed and stored.timestamp < cutoff:
                logger.info("Purged data with secret %r", key)
                del self.data[key]
        self._last_purge = int(time.time())
        logger.info("Finished purging data.")
class ContentGroupManager:
    """ """

    def __init__(self):
        self.__groups_collection = PersistentMapping()

    def __add_group_item(self, id, name, filter, pattern):
        #create a new item
        item = ContentGroup(id, name, filter, pattern)
        self.__groups_collection[id] = item

    def __update_group_item(self, id, name, filter, pattern):
        #modify an item
        item = self.__groups_collection.get(id)
        if item is not None:
            item.name =     name
            item.filter =   filter
            item.pattern =  pattern
            self.__groups_collection[id] = item

    def __delete_group_item(self, id):
        #delete an item
        try:    del(self.__groups_collection[id])
        except: pass


    #################
    #   BASIC API   #
    #################
    def get_groups_collection(self):
        #return the groups collection
        return self.__groups_collection

    def get_groups_ids(self):
        #get the groups ids
        return self.__groups_collection.keys()

    def get_groups_list(self):
        #get a list with all items
        return utils.utSortObjsListByAttr(self.__groups_collection.values(), 'name')

    def get_group_item(self, id):
        #get an item
        try:    return self.__groups_collection[id]
        except: return None

    def get_group_item_data(self, id):
        #get an item data
        item = self.get_group_item(id)
        if item is not None: 
            return ['update',
                    item.id,
                    item.name,
                    item.filter,
                    item.pattern,
                    item.start,
                    item.maxResults,
                    item.g_filter,
                    item.restrict,
                    item.safeSearch,
                    item.language,
                    item.inputencoding,
                    item.outputencoding,
                    item.http_proxy,
                    item.search_type]
        else:
            return ['add', '', '', '', '']

    def add_group_item(self, id, name, filter, pattern):
        #create a new item
        self.__add_group_item(id, name, filter, pattern)

    def update_group_item(self, id, name, filter, pattern):
        #modify an item
        self.__update_group_item(id, name, filter, pattern)

    def update_google_props(self, id, start, maxResults, filter, restrict, safeSearch,
                            language, inputencoding, outputencoding, http_proxy):
        #update the Google search properties
        msg = 0
        item = self.__groups_collection.get(id)
        if item is not None:
            #set data
            try:
                #check if integer values
                start =            int(start)
                maxResults =       int(maxResults)
                filter =           int(filter)
                safeSearch =       int(safeSearch)
            except:
                msg = 1
            if not msg:
                #set data
                item.start =            start
                item.maxResults =       maxResults
                item.g_filter =         filter
                item.safeSearch =       safeSearch
                item.restrict =         restrict
                item.language =         language
                item.inputencoding =    inputencoding
                item.outputencoding =   outputencoding
                item.http_proxy =       http_proxy
                self.__groups_collection[id] = item
        return msg

    def update_search_type(self, id, search_type):
        #update the Google search type
        item = self.__groups_collection.get(id)
        item.search_type = search_type
        self.__groups_collection[id] = item

    def delete_group_item(self, ids):
        #delete 1 or more items
        map(self.__delete_group_item, ids)

    security = ClassSecurityInfo()
    security.setDefaultAccess("allow")
class NounBayesClassifier(Persistent):
    """
    """
    implements(IContentClassifier)
    
    def __init__(self,tagger=None,noNounRanksToKeep = 20):
        """
        """
        self.noNounRanksToKeep = noNounRanksToKeep
        self.trainingDocs = PersistentMapping()
        self.allNouns = OOSet()
        
        self.classifier = None
        self.trainAfterUpdate = True
    
    def addTrainingDocument(self,doc_id,tags):
        """
        """
        storage = getUtility(INounPhraseStorage)
        importantNouns = storage.getNounTerms(doc_id,self.noNounRanksToKeep)
        
        self.trainingDocs[doc_id] = (importantNouns,tags)
        self.allNouns = union(self.allNouns,OOSet(importantNouns))
    
    def train(self):
        """
        """
        presentNouns = dict()
        trainingData = []
        if not self.allNouns:
            storage = getUtility(INounPhraseStorage)
            for key in self.trainingDocs.keys():
                importantNouns = storage.getNounTerms(
                    key,
                    self.noNounRanksToKeep)
                self.allNouns = union(self.allNouns,OOSet(importantNouns))
        for item in self.allNouns:
            presentNouns.setdefault(item,0)
        
        for (nouns,tags) in self.trainingDocs.values():
            nounPresence = presentNouns.copy()
            for noun in nouns:
                nounPresence[noun] = 1
            for tag in tags:
                trainingData.append((nounPresence,tag,))
        if trainingData:
            self.classifier = NaiveBayesClassifier.train(trainingData)
    
    def classify(self,doc_id):
        """
        """
        if not self.classifier:
            return []
        
        presentNouns = dict()
        for item in self.allNouns:
            presentNouns.setdefault(item,0)
        
        storage = getUtility(INounPhraseStorage)
        importantNouns = storage.getNounTerms(doc_id,self.noNounRanksToKeep)
        for noun in importantNouns:
            if noun in presentNouns.keys():
                presentNouns[noun] = 1
        return self.classifier.classify(presentNouns)
    
    def probabilityClassify(self,doc_id):
        """
        """
        if not self.classifier:
            return []
        
        presentNouns = dict()
        for item in self.allNouns:
            presentNouns.setdefault(item,0)
        storage = getUtility(INounPhraseStorage)
        importantNouns = storage.getNounTerms(doc_id,self.noNounRanksToKeep)
        for noun in importantNouns:
            if noun in presentNouns.keys():
                presentNouns[noun] = 1
        return self.classifier.prob_classify(presentNouns)
    
    def clear(self):
        """Wipes the classifier's data.
        """
        self.allNouns.clear()
        self.trainingDocs.clear()
    
    def tags(self):
        if not self.classifier:
            return []
        return self.classifier.labels()
        
class CustomField(Folder):
    """
    A CustomField is an object that becomes automatically included
    as part of the Add Issue page. The ID of the custom field becomes the
    name of the input. So if the ID is 'foo' the input rendered becomes
    <input name="foo">
    
    This class defines:
        
    Type of input
    ---------------------------------------------------------------------------
    
      You can select one of the following:
          text, textarea, password, hidden, select, checkbox, radio or file
          
      Depending on which one you select you'll specify parameters such as 
      'cols' (for type 'textarea' of course) or size. By having first selected
      a type, the field will autogenerate some default parameters that you can
      later modify.
    
    
    Default value
    ---------------------------------------------------------------------------
    
      The default value can be either a simple string inputted or it can be a
      reference to something else callable that will get the default value and
      this is done with a TALES expression. 
    
    Being mandatory or optional
    ---------------------------------------------------------------------------
    
      By default every field is optional but by making it mandatory, you'll 
      most likely going to have to specify a validation because sometimes it's 
      not as simple as checking that a value is boolean or not (e.g. bool(''))
    
    Validation
    ---------------------------------------------------------------------------
    
      This is where you specify either a reference to a script or a TALES 
      expression that will work out if a particular value is valid or not. 
    
    Javascript events hooks (onchange, onclick, onfocus, onblur)
    ---------------------------------------------------------------------------
    
      You'll be responsible for what you write in the values for these. The 
      values must be available javascript functions.
    
    Setting persistent values on issues
    ---------------------------------------------------------------------------
    
      (This is actually implemented in IssueTrackerProduct/IssueTracker.py)
    
      When saving the issue, we'll add an attribute to the issue like this::
          
          <id of custom field>: <value at input>
          
      This will pass through the validation a second time but unlike the first
      time, if the validation fails this time a hard error is raised. The type
      of the value is by default a unicode string or what else is appropriate 
      based on the input type. 
      You can specify an expression that will massage the input before it's 
      saved. So, suppose you want to save it as a floating point number you
      enter this expression::
          
          python:float(value)
          
    Getting persistent values on issues
    ---------------------------------------------------------------------------
    
      (This is actually implemented in IssueTrackerProduct/IssueTracker.py)

      You can ask the issuetracker for the value of a custom field simply by
      specifying the ID of the custom field and an optional default value. 
      Quite possibly you'll have an issuetracker where issues were added before
      the creation of the custom field so it'll be important to supply a 
      default value.
    
    Additionally loaded Javascript and CSS
    ---------------------------------------------------------------------------
    
      You get an area for entering the Javascript and the CSS and this is 
      automatically loaded on the Add Issue page. If you in your input of this
      (on the first line) enter a name of a file or DTML Method/Document that
      exists, that is instead rendered.
      The input can also be a valid URL if it looks relative and valid.
    
    """

    meta_type = CUSTOMFIELD_METATYPE

    manage_options = (
        {"label": "Manage", "action": "manage_field"},
        {"label": "Validation", "action": "manage_validation"},
    ) + Folder.manage_options

    _properties = (
        {"id": "title", "type": "ustring", "mode": "w"},
        {"id": "disabled", "type": "boolean", "mode": "w"},
        {"id": "python_type", "type": "selection", "mode": "w", "select_variable": "getOKPythonTypes"},
        {"id": "include_in_filter_options", "type": "boolean", "mode": "w"},
    )

    security = ClassSecurityInfo()

    def __init__(
        self,
        id,
        title=u"",
        input_type="text",
        python_type="ustring",
        extra_js=u"",
        extra_css=u"",
        mandatory=False,
        options=[],
        options_expression="",
        visibility_expression="",
        include_in_filter_options=False,
    ):
        self.id = str(id)
        self.title = title
        self.input_type = input_type
        self.python_type = python_type
        self.attributes = PersistentMapping()
        self.extra_css = extra_css
        self.extra_js = extra_js
        self.mandatory = mandatory
        self.options = options
        self.options_expression = options_expression
        self.disabled = False
        self.visibility_expression = visibility_expression
        self.include_in_filter_options = include_in_filter_options

    ##
    ## Attributes of the object
    ##

    def getId(self):
        return self.id

    def getTitle(self):
        return self.title

    def isMandatory(self):
        return self.mandatory

    def isDisabled(self):
        return self.disabled

    def getOptions(self):
        return self.options

    def getInputType(self):
        return self.input_type

    def getPythonType(self):
        return self.python_type

    security.declareProtected(VMS, "getOptionsFlat")

    def getOptionsFlat(self):
        """ return the list of options with a | pipe sign to split tuples """
        return list_to_flat(self.getOptions())

    def getOptionsExpression(self):
        """ true if it looks like a TALES expression """
        return self.options_expression

    def getVisibilityExpression(self):
        return self.visibility_expression

    def includeInFilterOptions(self):
        return self.include_in_filter_options

    ##
    ## Special Zope magic
    ##

    def getOKPythonTypes(self):
        return OK_python_types

    ##
    ## Special massaging on the class attributes
    ##

    def _prepareByType(self):
        """ set all the appropriate default bits and pieces by the 
        input_type. For example, if the input type is 'textarea' set a
        default cols and rows.
        """
        if self.input_type == "textarea":
            self.attributes["cols"] = DEFAULT_TEXTAREA_COLS
            self.attributes["rows"] = DEFAULT_TEXTAREA_ROWS

        elif self.input_type == "checkbox":
            pass
        # if 'value' in self.attributes:
        #     del self.attributes['value']

        elif self.input_type == "radio":
            if "value" in self.attributes:
                del self.attributes["value"]

        elif self.input_type == "file":
            if "value" in self.attributes:
                del self.attributes["value"]

    ##
    ## Rendering stuff
    ##

    def render(self, *value, **extra_attributes):
        """ return the tag (e.g. <textarea>) and any other accompanying
        HTML stuff. 
        """

        # if someone passes None as the first and only parameter to render()
        # the value of variable 'value' will be (None,)
        # This should be considered as if nothing is set
        if value == (None,):
            value = []

        if value and isinstance(value[0], InstanceType) and value[0].__class__.__name__ == "HTTPRequest":
            # this method has been called with REQUEST as the value parameter.
            # Note that it's still a list or tuple but convert it to the actual value.
            value = value[0].form.get(self.getId(), None)
            if value is None:
                value = ()
            else:
                value = (value,)  # make sure it's a tuple

        out = []
        if DevelopmentMode:
            out.append(u"<!--CustomField: %s -->" % self.getId())

            if self.isDisabled():
                logger.warn("A disabled custom field (%s) is rendered" % self.absolute_url_path())

        # take out some extra keywords from the extra_attributes
        skip_extra_css = extra_attributes.pop("skip_extra_css", False)
        skip_extra_js = extra_attributes.pop("skip_extra_js", False)

        if self.extra_css and not skip_extra_css:
            out.append(self.render_extra_css())
        if self.extra_js and not skip_extra_js:
            out.append(self.render_extra_js())

        out.append(self.render_tag(*value, **extra_attributes))

        return "\n".join(out)

    def render_tag(self, *value, **extra_attributes):
        """ return a piece of unicode HTML that """

        assert len(value) <= 1, "Can't pass more than one argument as value"

        inner = []
        attributes = {}
        # notice the order of these update() calls! It matters

        name_prefix = extra_attributes.pop("name_prefix", "")

        # It's an option to called render_tag() with in_filter=True which tells
        # us that this tag is rendered as a filter, in the filter options.
        # This is something that can be done on-the-fly and it means that
        # certain things should work differently. For example, a 'select' type
        # input get's an added 'size' and 'multiple' attribute when used as a
        # filter.
        in_filter = extra_attributes.pop("in_filter", False)

        # core attributes
        dom_id = self.attributes.get("dom_id", "id_%s" % self.getId())
        attributes.update({"name": self._wrapPythonTypeName(name_prefix), "id": dom_id, "title": self.getTitle()})

        # saved attributes
        attributes.update(dict(self.attributes))

        # extra on rendering attributes
        attributes.update(extra_attributes)

        # Now, "hack" the attributes if this is used in a filter
        if in_filter:
            if self.input_type == "select":
                attributes["multiple"] = "multiple"
                if "size" not in attributes:
                    attributes["size"] = min(5, len(list(self.getOptionsIterable())))

        # filler is a dict that we will use to render the template
        filler = {}

        if self.input_type == "textarea":
            template = u"<textarea %(inner)s>%(value)s</textarea>"

            v = None
            if value:
                v = value[0]  # from the argument
            elif "value" in attributes:
                v = attributes.pop("value")

            if v:
                filler["value"] = Utils.safe_html_quote(v)
            else:
                filler["value"] = u""

        elif self.input_type == "select":
            template = u"<select %(inner)s>\n%(all_options)s\n</select>"
            all_options = []

            v = []
            if value:
                v = value[0]
                # makes sure the value doesn't become a nested list
                if isinstance(v, list):
                    v = Utils.flatten_lines(v)

            elif "value" in attributes:
                v = attributes.pop("value")

            if not isinstance(v, (tuple, list)):
                v = [v]

            # if the value passed to render this select contains
            # items that are not in the list of options, don't
            # use the list of options.
            _values_selected = []
            for option in self.getOptionsIterable():
                if isinstance(option, (tuple, list)):
                    value, label = option
                else:
                    value, label = option, option

                if self.getPythonType() == "int":
                    try:
                        value = int(value)
                    except ValueError:
                        pass
                elif self.getPythonType() == "float":
                    try:
                        value = float(value)
                    except ValueError:
                        pass

                if value in v:
                    tmpl = u'<option value="%s" selected="selected">%s</option>'
                    _values_selected.append(value)
                else:
                    tmpl = u'<option value="%s">%s</option>'

                all_options.append(tmpl % (value, label))

            if Set(v) - Set(_values_selected):
                # there were values that weren't in the list of options!
                _values_not_in_options = list(Set(v) - Set(_values_selected))

                # if nothing was matched in the list of options,
                # reset the whole all_options list.
                if not _values_selected and all_options:
                    all_options = []

                for value in _values_not_in_options:
                    label = value
                    tmpl = u'<option value="%s" selected="selected">%s</option>'
                    all_options.append(tmpl % (value, label))

            filler["all_options"] = "\n".join(all_options)

        elif self.input_type == "radio":
            # special case
            if not self.getOptionsIterable():
                template = u"ERROR: No options"
            else:
                template = u"%(all_inputs)s"
                all_inputs = []

                v = None
                if value:
                    v = value[0]  # from the argument

                elif "value" in attributes:
                    v = attributes.pop("value")

                special_attributes = ""
                inner = []
                for k, v2 in attributes.items():
                    if k in ("id",):
                        continue
                    inner.append('%s="%s"' % (k, v2))
                if inner:
                    special_attributes = " " + " ".join(inner)

                for option in self.getOptions():
                    if isinstance(option, (tuple, list)):
                        value, label = option
                    else:
                        value, label = option, option

                    if value == v:
                        tmpl = u'<input type="radio" value="%s" checked="checked"%s /> %s<br />'
                    else:
                        tmpl = u'<input type="radio" value="%s"%s/> %s<br />'
                    all_inputs.append(tmpl % (value, special_attributes, label))

                filler["all_inputs"] = "\n".join(all_inputs)

        elif self.input_type == "checkbox":
            # another special case

            v = None
            if value:
                v = value[0]  # from the argument
            elif "value" in attributes:
                v = attributes.pop("value")

            # If there are no options you can work this like a normal text input
            if not self.getOptions():
                # but what if it should be a boolean and it's true, then the
                # tag needs to contain checked="checked"
                if v:
                    template = u'<input type="checkbox" checked="checked" %(inner)s />'
                else:
                    template = u'<input type="checkbox" %(inner)s />'

            else:
                # crap!
                template = u"%(all_inputs)s"
                all_inputs = []

                special_attributes = ""
                inner = []
                for k, v2 in attributes.items():
                    if k in ("id",):
                        continue
                    inner.append('%s="%s"' % (k, v2))
                if inner:
                    special_attributes = " " + " ".join(inner)

                for option in self.getOptions():
                    if isinstance(option, (tuple, list)):
                        value, label = option
                    else:
                        value, label = option, option

                    if value == v:
                        tmpl = u'<input type="checkbox" value="%s" checked="checked"%s /> %s<br />'
                    else:
                        tmpl = u'<input type="checkbox" value="%s"%s/> %s<br />'
                    all_inputs.append(tmpl % (value, special_attributes, label))

                filler["all_inputs"] = "\n".join(all_inputs)

        elif self.input_type == "password":

            template = u'<input type="password" %(inner)s />'

        elif self.input_type == "file":
            template = u'<input type="file" %(inner)s />'

        else:  # type text
            template = u"<input %(inner)s />"

        if not (self.input_type == "radio" or (self.input_type == "checkbox" and self.getOptions())):

            if value and self.input_type not in ("select",):
                if value and value[0]:
                    # This overrides the default value
                    attributes["value"] = value[0]

            for key, val in sorted(attributes.items()):
                inner.append('%s="%s"' % (key, val))

            filler["inner"] = " ".join(inner)

        return template % filler

    def __str__(self):
        return str(self.render())

    def _wrapPythonTypeName(self, prefix=""):
        """ if name is 'age' and python_type is 'int' then return
        'age:int'.
        If the type is unicode type, add the encoding
        """
        name, python_type = self.getId(), self.python_type

        # add the prefix
        name = "%s%s" % (prefix, name)

        if self.input_type == "file":
            # exception
            return name

        if python_type in ("ustring", "ulines"):
            return "%s:%s:%s" % (name, UNICODE_ENCODING, python_type)
        elif python_type == "string":
            return name
        else:
            return "%s:%s" % (name, python_type)

    def render_extra_css(self):
        """ return a piece of HTML that loads the CSS.
        If it looks like the attribute self.extra_css is a URI,
        return a <link rel="stylesheet"> tag instead.
        """
        css = self.extra_css
        if len(css.splitlines()) == 1 and (css.startswith("http") or css.startswith("/") or css.endswith(".css")):
            return u'<link rel="stylesheet" type="text/css" href="%s" />' % css
        elif css:
            return u'<style type="text/css">\n%s\n</style>' % css
        else:
            return u""

    def render_extra_js(self):
        """ return a piece of HTML that loads the javascript.
        If it looks like the attribute self.extra_js is a URI,
        return a <script src="..."> tag instead.
        """
        js = self.extra_js
        if len(js.splitlines()) == 1 and (js.startswith("http") or js.startswith("/") or js.endswith(".js")):
            return u'<script type="text/javascript" src="%s"></script>' % js
        elif js:
            return u'<script type="text/javascript">\n%s\n</script>' % js
        else:
            return u""

    security.declareProtected(VMS, "preview_render")

    def preview_render(self, *value, **extra_attributes):
        """ wrapper on render() that is able to cut out some of the verbose stuff
        from the render output.
        """
        html = self.render(*value, **extra_attributes)
        return html

    ##
    ## TALES expression for options
    ##

    def getOptionsIterable(self):
        """ return a list of options """
        if self.getOptionsExpression():
            ec = self._getExprContext(self)
            ex = Expression(self.options_expression)
            return list(ex(ec))
        else:
            return self.getOptions()

    def _getExprContext(self, object, extra_namespaces={}):
        return getExprContext(self, object, extra_namespaces=extra_namespaces)

    def _valid_options_expression(self):
        """ return true if self.options_expression is valid 
        otherwise raise an error. 
        """
        ec = self._getExprContext(self)
        ex = Expression(self.options_expression)
        iterable = ex(ec)

        if isinstance(iterable, (list, tuple)):
            # each item should be unicodeable and
            # every item must something
            for item in iterable:
                if isinstance(item, (tuple, list)):
                    key, value = item
                    if key and not value:
                        value = key
                else:
                    key, value = item, item
                    if not item:
                        return False

            # an iterable we can't find anything wrong with
            return True

        # default is not to pass
        return False

    ##
    ## Validation
    ##

    def getValidationExpressions(self):
        return self.objectValues(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)

    security.declarePrivate("testValidValue")

    def testValidValue(self, value):
        """ return a tuple of (valid or not [bool], message [unicode]) if the value
        passes all the validation expressions (assuming the field has any)
        """
        # check the python type
        if self.python_type == "ustring":
            # should be possible to do this
            try:
                unicode(value)
            except TypeError:
                return False, u"Not a unicode string"
        elif self.python_type == "int":
            try:
                int(value)
            except ValueError:
                return False, u"Not an integer number"
        elif self.python_type == "float":
            try:
                float(value)
            except ValueError:
                return False, u"Not a floating point number"
        elif self.python_type == "long":
            try:
                long(value)
            except ValueError:
                return False, u"Not a long integer number"
        elif self.python_type == "date":
            try:
                if isinstance(value, basestring):
                    DateTime(value)
            except DateError:
                return False, u"Not a valid date"
        elif self.python_type == "ulines":
            if isinstance(value, basestring):
                try:
                    [unicode(x) for x in value.splitlines()]
                except ValueError:
                    return False, u"Not a list of unicode strings"
            elif value is not None:
                value = Utils.flatten_lines(value)
                try:
                    [unicode(x) for x in value]
                except ValueError:
                    return False, u"Not a list of unicode strings"
        elif self.python_type == "lines":
            if isinstance(value, basestring):
                try:
                    [str(x) for x in value.splitlines()]
                except ValueError:
                    return False, u"Not a list of strings"
            elif value is not None:
                value = Utils.flatten_lines(value)
                try:
                    [str(x) for x in value]
                except ValueError:
                    return False, u"Not a list of strings"

        # check each TALES expression
        for ve in self.getValidationExpressions():
            ec = self._getExprContext(self, extra_namespaces=dict(value=value))
            ex = Expression(ve.expression)
            if not bool(ex(ec)):
                return False, ve.message

        # by default no validation expression made it invalid
        return True, None

    ##
    ## Working with the persistent attributes
    ##

    def getCoreAttribute(self, *key_and_default):
        """ return the value of this attribute. If len(@key_and_default) = 2 is
        the second one is a default value. If not don't fall back on a default.
        """
        if not len(key_and_default) in (1, 2):
            raise ValueError, "Call getCoreAttribute(key [,default])"

        if len(key_and_default) == 1:
            return self.attributes[key_and_default[0]]
        else:
            return self.attributes.get(key_and_default[0], key_and_default[1])

    security.declareProtected(VMS, "getCoreAttributeKeys")

    def getCoreAttributeKeys(self):
        return list(self.attributes.keys())

    security.declareProtected(VMS, "getCoreAttributeKeyLabel")

    def getCoreAttributeKeyLabel(self, key, html_ok=False):
        """ return a string that explains what the key is.
        The resturn string can contain HTML.
        """
        if key == "dom_id":
            if html_ok:
                return u'<abbr title="DOM element ID, not Zope object ID">DOM ID</abbr>'
            else:
                return u"DOM ID"

        if key.startswith("on") and re.findall("on\w+", key):
            return u"on" + key[2:].capitalize()

        if key in ("rows", "cols"):
            return u"Textarea %s" % key

        return key.title()

    def getCoreAttributeKeySuggestions(self):
        """ return a list of suggestions of attribute keys you might want to add """
        suggestions = ["style", "size", "dom_id", "onchange", "onkeypress", "onclick", "onfocus", "onblur", "value"]
        # add more
        if self.input_type == "textarea":
            suggestions.append("cols")
            suggestions.append("rows")
        elif self.input_type == "select":
            suggestions.append("multiple")

        # reduce already used ones
        suggestions = [x for x in suggestions if x not in self.attributes]

        # sort them by their labels
        suggestions = [(self.getCoreAttributeKeyLabel(x), x) for x in suggestions]
        suggestions.sort()

        # return just the keys
        return [x[1] for x in suggestions]

    def getCoreAttributeKeyName(self, key):
        """ return what the suitable name for the key should be a input tag
        """
        return u"%s:ustring" % key

    def getDeleteableAttributeKeys(self):
        """ return a list of keys of attributes you can delete """
        all = Set(list(self.attributes.keys()))
        not_ = Set(CORE_ATTRIBUTES)
        return list(all - not_)

    ##
    ## Modifying the custom field
    ##

    security.declareProtected(VMS, "manage_saveFieldProperties")

    def manage_saveFieldProperties(
        self,
        input_type=None,
        python_type=None,
        title=None,
        mandatory=False,
        extra_css=None,
        extra_js=None,
        options=None,
        options_expression=None,
        visibility_expression=None,
        include_in_filter_options=False,
        REQUEST=None,
        **settings
    ):
        """ saving changes via the web """
        if input_type is not None:
            different = input_type != self.input_type

            if not input_type in OK_input_types:
                raise ValueError, "invalid input_type"
            self.input_type = input_type
            if different:
                self._prepareByType()

        if python_type is not None:
            assert python_type in OK_python_types, "Invalid Python type (%r)" % python_type
            self.python_type = python_type

        if title is not None:
            self.title = unicode(title)

        self.mandatory = bool(mandatory)
        self.include_in_filter_options = bool(include_in_filter_options)

        if extra_css is not None:
            self.extra_css = unicode(extra_css).strip()

        if extra_js is not None:
            self.extra_js = unicode(extra_js).strip()

        if options_expression is not None:
            self.options_expression = str(options_expression).strip()

            if self.options_expression:
                assert self._valid_options_expression(), "Invalid expression"

        if visibility_expression is not None:
            self.visibility_expression = visibility_expression

        if options is not None:
            self.options = flat_to_list(options)

        if not settings and REQUEST is not None:
            settings = self.REQUEST.form

        # I don't like the pattern but it'll have to do for now
        for key, value in settings.items():
            if key not in CORE_ATTRIBUTES:
                self.attributes[key] = value

        if REQUEST is not None:
            msg = "Changes saved"
            url = self.absolute_url() + "/manage_field"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_addFieldProperty")

    def manage_addFieldProperty(self, key=None, new_key=None, REQUEST=None):
        """ add a new attribute property """
        if not key and not new_key:
            raise ValueError, "must pass 'key' OR 'new_key'"

        if new_key:
            key = new_key.strip()

        key = str(key)

        self.attributes[key] = u""

        if REQUEST is not None:
            msg = "Field added"
            url = self.absolute_url() + "/manage_field"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            url += "#field-%s" % key
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_deleteFieldProperty")

    def manage_deleteFieldProperty(self, key, REQUEST=None):
        """ delete a field property """
        del self.attributes[key]

        if REQUEST is not None:
            msg = "Attribute deleted"
            url = self.absolute_url() + "/manage_field"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_addValidationExpression")

    def manage_addValidationExpression(self, expression, message=u"", REQUEST=None):
        """ add a new validation expression """
        # check that it's not complete rubbish
        expression = str(expression).strip()
        message = unicode(message).strip()
        if not expression:
            raise ValueError, "Expression can't be empty"

        # XXX: Got to figure out a better way to test the expression without a
        # arbitrary value like this
        ## test it
        # ec = self._getExprContext(self, extra_namespaces=dict(value='123'))
        # ex = Expression(expression)
        # try:
        #    ex(ec)
        # except Exception, m:
        #    raise ValueError, m

        c = len(self.objectIds(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)) + 1
        oid = "validation_%s" % c
        while base_hasattr(self, oid):
            c += 1
            oid = "validation_%s" % c

        instance = ValidationExpression(oid, expression, message)
        self._setObject(oid, instance)

        if REQUEST is not None:
            msg = "Expression added"
            url = self.absolute_url() + "/manage_validation"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_deleteValidationExpression")

    def manage_deleteValidationExpression(self, id, REQUEST=None):
        """ delete a validation expression """
        assert id in self.objectIds(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)

        self.manage_delObjects([id])

        if REQUEST is not None:
            msg = "Expression delete"
            url = self.absolute_url() + "/manage_validation"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_editValidationExpression")

    def manage_editValidationExpression(self, id, expression, message, delete=False, REQUEST=None):
        """ change a validation expression object """
        assert id in self.objectIds(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)
        obj = getattr(self, id)

        if delete:
            return self.manage_deleteValidationExpression(id, REQUEST=REQUEST)

        expression = str(expression).strip()
        message = unicode(message).strip()
        if not expression:
            raise ValueError, "Expression can't be empty"

        # test it
        ec = self._getExprContext(self, extra_namespaces=dict(value="123"))
        ex = Expression(expression)
        try:
            ex(ec)
        except Exception, m:
            raise ValueError, m

        obj.expression = expression
        obj.message = message

        if REQUEST is not None:
            msg = "Expression changed"
            url = self.absolute_url() + "/manage_validation"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)
Exemple #14
0
class WeightNode(Node):
    'A traditional model neuron: state in [0,1], weighted inputs'

    def __init__(self, par, weightDomain=(-7, 7), quanta=None, abs_weights=0):
        # with absolute weights and polarity neurons range must be >= 0.
        if abs_weights:
            assert weightDomain[0] >= 0
        Node.__init__(self, par)
        self.weights = PersistentMapping()
        self.weightDomain = weightDomain
        self.quanta = quanta
        self.output = random.uniform(0, 1)
        # abs_weights only affects external inputs since weightDomain is forced
        # to be positive above
        self.abs_weights = abs_weights

    def destroy(self):
        Node.destroy(self)
        del self.weights

    def mutate(self, p):
        mutations = 0
        for src in self.weights.keys():
            if random.random() < p:
                mutations += 1
                self.weights[src] = rdom(self.weightDomain, self.weights[src],
                                         self.quanta)
        return mutations

    def addInput(self, source):
        Node.addInput(self, source)
        self.weights[source] = rdom(self.weightDomain, None, self.quanta)

    def addExternalInput(self, s_bp, s_sig, weight):
        'source = (srcBodypart, srcSignal, weight)'
        source = (s_bp, s_sig)
        if isinstance(s_sig, Node):
            assert s_sig in s_bp.network
        Node.addExternalInput(self, source)
        if self.abs_weights:
            weight = abs(weight)
        self.weights[source] = weight

    def removeExternalInput(self, bp, sig):
        'source = (srcBodypart, srcSignal)'
        Node.removeExternalInput(self, bp, sig)
        del self.weights[(bp, sig)]

    def delInput(self, source):
        Node.delInput(self, source)
        del self.weights[source]

    def check(self):
        for key in self.weights.keys():
            assert key in self.inputs
        for src in self.inputs:
            assert src in self.weights

    def fixup(self):
        # remove all weights that dont have an input
        for weight_key in self.weights.keys():
            if weight_key not in self.inputs:
                del (self.weights[weight_key])
        # add weight for any inputs that dont have one
        for source in self.inputs:
            if source not in self.weights:
                weight = self._chooseRandom()
                self.weights[source] = weight

    def swapInputs(self, a, b):
        Node.swapInputs(self, a, b)
        if a in self.weights and b in self.weights:
            t = self.weights[a]
            self.weights[a] = self.weights[b]
            self.weights[b] = t
        elif a in self.weights:
            self.weights[b] = self.weights[a]
            del self.weights[a]
        elif b in self.weights:
            self.weights[a] = self.weights[b]
            del self.weights[b]

    def wsum(self, inputs=None, use_external=1):
        'sum of weighted inputs'
        cumulative = 0
        if inputs == None:
            inputs = self.inputs
        # quantise inputs
        for src in inputs:
            x = src.output
            if self.quanta:
                x = quantise(x, self.quanta)
            if isinstance(src, SrmNode):
                # hack since output is spikes, we want eps function
                # note that we don't do quantisation here.
                # note: with async nodes, eps may not have been set yet.
                if hasattr(src, 'eps'):
                    x = src.eps
            cumulative += x * self.weights[src]
        if use_external:
            for (src, x) in self.externalInputs.items():
                if self.quanta:
                    x = quantise(x, self.quanta)
                cumulative += x * self.weights[src]
        return cumulative
Exemple #15
0
class HasEmail:
    """Mixin class proving email address(es) and related functions"""
    
    def __init__(self):
        self.__items = []
        self.__unconfirmed = PersistentMapping()
        self.primary_email = None
        
    def is_valid_email(cls, email):
        """Class method returns True if email is valid, or False if it should
        be rejected.

        >>> HasEmail.is_valid_email('*****@*****.**')
        True
        >>> HasEmail.is_valid_email('*****@*****.**')
        True
        >>> HasEmail.is_valid_email('*****@*****.**')
        True
        >>> HasEmail.is_valid_email('xyz')
        False
        >>> HasEmail.is_valid_email('abc@xyz@foo')
        False
        """
        global _blocked_domains
        
        if email.find('@') == -1:
            return False
        
        if email.count('@') != 1:
            return False
            
        (username, host) = email.split('@')
        if host in _blocked_domains:
            return False
        
        return True
        
    is_valid_email = classmethod(is_valid_email)
        
    def add_email(self, email):
        """Add email to the list. Adds primary if none set."""
        email = email.lower()
        if email not in self.__items:
            self.__items.append(email)
            self._p_changed = 1

        if self.primary_email is None:
            self.primary_email = email
            
    def add_unconfirmed_email(self, email):
        """Add new e-mail that has not yet been confirmed. Call confirm_email to move
        into active list of e-mails.
        Returns confirmation code that must be given to confirm_email to confirm.
        """
        email = email.lower()
        if not self.__unconfirmed.has_key(email):
            self.__unconfirmed[email] = _password_generator.generate(seed=email)
        return self.__unconfirmed[email]
        
    def remove_unconfirmed_email(self, email):
        email = email.lower()
        if self.__unconfirmed.has_key(email):
            del self.__unconfirmed[email]
            
    def confirm_email(self, code):
        """Confirm email with the given code, or return False if invalid code."""
        for email, conf_code in self.__unconfirmed.items():
            if conf_code == code:
                self.add_email(email)
                del self.__unconfirmed[email]
                self.notify_email_confirmed(email)
                return email
        return None
    
    def remove_email(self, email):
        """Remove an e-mail address from the list. Raises KeyError if only one e-mail address left"""
        email = email.lower()
        if self.__unconfirmed.has_key(email):
            return self.remove_unconfirmed_email(email)
            
        emails = self.email_list()
        if len(emails) > 1:
            self.__items.remove(email)
            self._p_changed = 1
            if email == self.get_primary_email():
                self.set_primary_email(self.email_list()[0])
        else:
            raise KeyError
            
    def remove_all_emails(self):
        self.__items = []
        self.primary_email = None
        
    def has_email(self, email):
        email = email.lower()
        return email in self.__items

    def email_list(self):
        return self.__items
        
    def unconfirmed_email_list(self):
        return self.__unconfirmed.keys()

    def set_primary_email(self, email):
        email = email.lower()
        if self.has_email(email):
            self.primary_email = email
        else:
            raise ValueError("I don't know email <%s>" % email)
            
    def get_primary_email(self):
        return self.primary_email
        
    def notify_email_confirmed(self, email):
        """Notice that email was just confirmed."""
        pass
        
    def _consistency_check(self):
        if self.primary_email is not None:
            if self.primary_email not in self.__items:
                raise KeyError, "primary_email not in email list"
            
        typecheck_seq(self.__items, str, allow_none=1)
Exemple #16
0
class Game(Persistent):
    """
    A Game aggregates the players and matches that are part of a competition. For example, a Game could be 'Football'
    or 'Hockey'
    """
    def __init__(self, name):
        self.name = name
        # Player name -> Player
        self.players = PersistentMapping()
        # List of all matches for this game
        self.matches = PersistentList()
        # Whether to use average instead of sum-of-skill for this game
        self.use_average_team_skill = True

    def delete_match(self, match):
        if not match in self.matches:
            return

        self.matches.remove(match)

        players = match.teams[0] + match.teams[1]
        for p in players:
            if match in p.matches:
                p.matches.remove(match)

        self.recalculate_ratings()

        for p in list(self.players.keys()):
            if not self.players[p].matches:
                self.players.pop(p)

    def add_match(self, teams, score):
        players_a = [self.get_player(name) for name in teams[0]]
        players_b = [self.get_player(name) for name in teams[1]]

        # Add Match to the Database
        match = Match(self, [players_a, players_b], score)
        self.matches.append(match)

        self.update_player_ratings(match)
        match.update_rating_delta()

    def update_player_ratings(self, match):
        ratings_a = [p.get_rating() for p in match.teams[0]]
        ratings_b = [p.get_rating() for p in match.teams[1]]

        # Sort by score and get rank indices
        rank = list(zip(match.score, range(len(match.score))))
        rank.sort(key=lambda r: r[0], reverse=True)
        rank_indices = list(zip(*rank))[1]

        # Check for Draw
        # TODO: make this generic for more than 2 teams
        if match.score[0] == match.score[1]:
            rank_indices = [0, 0]

        # Calculate new Ratings using trueskill algorithm
        new_ratings = trueskill.rate([ratings_a, ratings_b],
                                     ranks=rank_indices,
                                     weights=self.team_weights(
                                         ratings_a, ratings_b))

        for r, p in zip(new_ratings[0], match.teams[0]):
            p.set_rating(r)
            p.add_match(match)

        for r, p in zip(new_ratings[1], match.teams[1]):
            p.set_rating(r)
            p.add_match(match)

    def recalculate_ratings(self):
        for player in self.players.values():
            player.reset_rating()
            player.matches.clear()
            player.history.clear()

        for match in self.matches:
            match.init_stats()
            self.update_player_ratings(match)
            match.update_rating_delta()

    def get_player(self, name):
        if not name in self.players:
            self.players[name] = Player(name)

        return self.players[name]

    # Calcualte player weights for a match based on whether average or additive ratings
    # are used for this game
    def team_weights(self, team1, team2):
        ratings = [team1, team2]
        weights = [[1 for _ in r] for r in ratings]
        if self.use_average_team_skill:
            # Adjust weights by team-size. This effectively causes the TrueSkill algorithm to
            # look at the average instead of the sum of skills
            min_team_size = min(map(len, ratings))
            weights = [[min_team_size / float(len(r)) for _ in r]
                       for r in ratings]
        return weights

    def win_probability(self, team1, team2):
        """"
        Calculate the win probability of team1 over team2 given the skill ratings of
        all the players in the teams.
        """
        def skill_sum(team, weights):
            return sum([v.skill() * w for (v, w) in zip(team, weights)])

        weights = self.team_weights(team1, team2)
        delta_mu = skill_sum(team1, weights[0]) - skill_sum(team2, weights[1])
        sum_sigma = sum((r.confidence() * w)**2 for (
            r,
            w) in zip(itertools.chain(team1, team2), itertools.chain(
                *weights)))
        size = len(team1) + len(team2)
        denom = math.sqrt(size * (trueskill.BETA * trueskill.BETA) + sum_sigma)
        ts = trueskill.global_env()
        return ts.cdf(delta_mu / denom)

    def draw_probability(self, team1, team2):
        r1 = [p.get_rating() for p in team1]
        r2 = [p.get_rating() for p in team2]
        return trueskill.quality([r1, r2],
                                 weights=self.team_weights(team1, team2))
class ACL(Persistent):

    """ Access control hub. The ACL can be instantiated and added to, say
    the root object, with attribute name acl. """

    def __init__(self, settings):

        self.users = PersistentMapping()
        self.groups = PersistentMapping()
        self.activation = PersistentMapping()

        admin, pwd = settings.get('pycms.admin_user',
                                  "admin:admin").split(":")

        self.users['admin'] = User(admin, "Administrator", "", pwd)
        self.groups['admin'] = Group('admin', users=PersistentList(['admin']))
        self.groups['viewers'] = Group('viewers')
        self.groups['editors'] = Group('editors')

    def generate_user_invite_key(self, user_id):

        """ Generate unique registration key for user and set on user. """

        if not user_id in self.users:
            return None

        t1 = time.time()
        time.sleep(random.random())
        t2 = time.time()
        base = hashlib.md5(str(t1 + t2))
        key = base.hexdigest()[:KEY_LENGTH]

        self.activation[key] = self.users[user_id]

        return key

    def get_user_for_activation(self, key):

        return self.activation.get(key, None)

    def unset_activation_key(self, key):

        if key in self.activation:
            del self.activation[key]

    def list_users(self):

        """ Return a dict of users, using the id as key """

        return self.users.keys()

    def list_groups(self):

        return self.groups.keys()

    def update_user(self, **data):

        self.users[data['email']].name = data['name']
        if data.get('pwd', None):
            self.users[data['email']].set_pwd(data['pwd'])

    def set_user_groups(self, user_id, groups=[]):

        """ Remove user from all groups, and then reset..."""

        for group_id in self.groups.keys():
            self.rm_user_from_group(group_id, user_id)

        for group_id in groups:
            self.add_user_to_group(group_id, user_id)

    def rm_user_from_group(self, group_id, user_id):

        if user_id in self.groups[group_id].users:
            idx = self.groups[group_id].users.index(user_id)
            del self.groups[group_id].users[idx]

    def add_user_to_group(self, group_id, user_id):

        if not user_id in self.groups[group_id].users:
            self.groups[group_id].users.append(user_id)

    def create_user(self, email, pwd=None, name='', profile=None):

        if email in self.users:
            return None

        self.users[email] = User(email, name or email,
                                 email, pwd,
                                 profile=profile)

        return self.users[email]

    def remove_user(self, user_id):

        if user_id in self.users:
            del self.users[user_id]
Exemple #18
0
class PeopleDirectory(Folder):
    implements(IPeopleDirectory)
    title = 'People'

    def __init__(self):
        super(PeopleDirectory, self).__init__()
        self.categories = PersistentMapping()  # {id: PeopleCategory}
        self.categories.__parent__ = self
        self.catalog = CachingCatalog()
        self.catalog.document_map = DocumentMap()
        self.update_indexes()
        self.order = ()  # order of sections

        # Set up a default configuration
        self['all'] = section = PeopleSection('All')
        section['all'] = report = PeopleReport('All')
        report.set_columns(['name', 'organization', 'location', 'email'])
        self.set_order(['all'])

    def set_order(self, order):
        self.order = tuple(order)

    def update_indexes(self):
        indexes = {
            'lastfirst': CatalogFieldIndex(get_lastfirst),
            'lastnamestartswith': CatalogFieldIndex(get_lastname_firstletter),
            'texts': CatalogTextIndex(get_textrepr),
            # path index is needed for profile deletion
            'path': CatalogPathIndex2(get_path, attr_discriminator=get_acl),
            'allowed': CatalogKeywordIndex(get_allowed_to_view),
            'is_staff': CatalogFieldIndex(is_staff),
            'groups': CatalogKeywordIndex(get_groups),

            # provide indexes for sorting reports
            'department': CatalogFieldIndex(get_department),
            'email': CatalogFieldIndex(get_email),
            'location': CatalogFieldIndex(get_location),
            'organization': CatalogFieldIndex(get_organization),
            'phone': CatalogFieldIndex(get_phone),
            'position': CatalogFieldIndex(get_position),
            }

        # provide indexes for filtering reports
        for catid in self.categories.keys():
            getter = ProfileCategoryGetter(catid)
            indexes['category_%s' % catid] = CatalogKeywordIndex(getter)

        catalog = self.catalog
        need_reindex = False

        # add indexes
        for name, index in indexes.items():
            if name not in catalog:
                catalog[name] = index
                need_reindex = True

        # remove indexes
        for name in catalog.keys():
            if name not in indexes:
                del catalog[name]

        return need_reindex
Exemple #19
0
class Item(Persistent):

    def __init__(self, parent=None, name=None, title=None, description=None, overwrite=False, **kwargs):

        self.__parent = None
        self.__sid = self.get_new_sid()
        self.__name = None
        self.__title = None
        self.__description = None
        self.__dateTimeCreated = datetime.datetime.now()
        self.__dateTimeModified = datetime.datetime.now()

        self.name = name
        self.title = title
        self.description = description
        self.__properties = PersistentMapping()

        if parent:
            parent.add_item(self, overwrite=overwrite)

    def __repr__(self):
        class_ = self.__class__
        return repr('<%s.%s object at %#x with id:"%s" url:"%s">' % (class_.__module__, class_.__name__,
                                                                     id(self), self.id, self.url()
                                                                     )
                    )

    def mark_changed(self):
        self.__dateTimeModified = datetime.datetime.now()
        self._p_changed = 1

    @property
    def rename_able(self):
        """
        Check if I can be renamed, an item can be renamed only if it is not plugged to a folder
        @return True or False
        """

        if self.__parent is None:
            return True

        return False

    @property
    def name(self):
        return self.__name

    def validate_name(self, name):
        if not name or not self.is_string(name) or URL_SEPARATOR in name:
            return False

        return True

    @name.setter
    def name(self, name):
        # i will set my name only if me parent is None
        if self.rename_able:
            if self.validate_name(name):
                self.__name = name
            else:
                raise TypeError('item name not valid')
        else:
            raise NotRenameAbleError('i am in a folder, use folder.rename_item')

    # by default synonym of name
    def get_id_name(self):
        return 'name'

    def get_id(self):
        return getattr(self, self.get_id_name(), None)

    @property
    def id_name(self):
        return self.get_id_name()

    @property
    def id(self):
        return self.get_id()

    @property
    def sid(self):
        return self.__sid

    @property
    def title(self):
        if self.__title is None:
            return self.name
        else:
            return self.__title

    @title.setter
    def title(self, value):
        if (value and self.is_string(value)) or value is None:
            self.__title = value
        else:
            raise TypeError('title must string or None received %s' % type(value))

    @property
    def description(self):
        if self.__description is None:
            return self.title
        else:
            return self.__description

    @description.setter
    def description(self, value):
        if (value and self.is_string(value)) or value is None:
            self.__description = value
        else:
            raise TypeError('description must string or None received %s' % type(value))

    @property
    def created(self):
        return str(self.__dateTimeCreated)

    @property
    def modified(self):
        return str(self.__dateTimeModified)

    @property
    def root(self):
        if self.is_folder and self.parent is None:
            return True

        return False

    @property
    def is_folder(self):
        return False

    @property
    def parent(self):
        return self.__parent

    def inform_parent_attached(self, parent):
        """
        this is a private package function
        This function is called from folder containing this item
        to inform that it has been added to it's children collection
        @param parent: Folder
        @return: boolean
        """

        # I'll be attached to a parent folder only if i have no parent
        # and i must be already in the parent folder children collection
        if self.parent is None and parent.is_folder and self.name in parent:
            self.__parent = parent
            self.mark_changed()
            return True

        return False

    def inform_parent_detached(self):
        """
        this is a private package function
        This function is called from folder containing this item
        to inform that it has been removed from it's children collection
        @return: boolean
        """

        # i will detach my self from my parent only if i am no longer in my parent
        if self.parent is not None and self.name not in self.parent:
            self.__parent = None
            self.mark_changed()
            return True

        return False

    def allow_delete(self):
        """
        Check if I can be deleted
        @return True or False
        """

        return True

    def allow_move(self, target_folder):
        """
        Check if I am allowed to move to target folder
        @param target_folder: Folder
        @return True or False
        """

        return True

    def get_parents(self, validator=None):
        """
        :param validator interface or callable
        :return: list of parent until root reached or if validator is submitted, until parent validate
        """
        parents = []
        folder_parent = self.parent

        while folder_parent:
            parents.append(folder_parent)
            if validator:
                if (prefixes.is_interface(validator) and validator.providedBy(folder_parent)) or \
                        (not prefixes.is_interface(validator) and callable(validator) and validator(folder_parent)):

                    break

            folder_parent = folder_parent.parent

        return parents

    def is_folder_in_parents(self, folder_item):
        """
        Check whether i am in the hierarchy tree of folder
        for example /a/folder/v/c/item
        @param folder_item: Folder
        @return: boolean
        """
        if not isinstance(folder_item, folder.Folder):
            return False

        # go through my parents and try to find folder
        folder_parent = self.parent
        # go through the hierarchy of my parents and try to find folder
        while folder_parent:
            if folder_item == folder_parent:
                return True

            folder_parent = folder_parent.parent

        return False

    # properties operations
    def __getitem__(self, key):
        return self.get_property(key)

    def __delitem__(self, key):
        if key in self.__properties.keys():
            del self.__properties[key]
            self.mark_changed()

    def __setitem__(self, key, value):
        self.set_property(key, value)

    def has_property(self, name):
        return name in self.__properties

    def get_property(self, name, default=None):

        if name in ('name', 'id', 'title', 'description', 'created', 'modified'):
            return getattr(self, name)

        if name in self.__properties:
            return self.__properties[name]

        return default

    def set_property(self, name, value):
        if self.is_string(name, raise_err=True):
            self.__properties[name] = value
            self.mark_changed()

    def get_properties(self, properties):
        if not properties:
            return []

        if not isinstance(properties, (list, tuple)):
            raise TypeError('properties must be list or tuple received: %s' % type(properties))

        view = {}
        for key in properties:
            view[key] = self.get_property(key)

        return view

    def set_properties(self, properties):
        if properties is None:
            properties = dict()

        if not isinstance(properties, dict):
            raise TypeError('expected dict type, received %s' % type(properties))

        for name, value in properties.items():
            self.set_property(name, value)

        return True

    def get_url_item(self, url):
        """
        :param url: str, of type
        """

        if not url:
            url = URL_SEPARATOR

        if self.is_string(url, raise_err=True):
            #log_debug('get_url_item - url: %s' % url)
            sep_url = url.split(URL_SEPARATOR)
            ind = 0
            item = self
            len_sep_url = len(sep_url)
            for tu in sep_url:
                if tu == '' and ind == 0:
                    # if the first one is empty, this is the url separator
                    tu = URL_SEPARATOR

                elif tu == '':
                    # ignore empty ones
                    ind += 1
                    continue

                if prefixes.has_prefix(tu):
                    # I would like to pass the next item to some validators that can need it
                    #next_item_name
                    if ind < len_sep_url-1:
                        next_item_name = sep_url[ind+1]
                    else:
                        next_item_name = None

                    item = prefixes.get_parent(tu, item, next_item_name=next_item_name)
                    #log_debug('... get parent from prefix "%s"' % tu)

                    if item is None:
                        #log_debug('... parent from prefix "%s" not found' % tu)
                        break

                    ind += 1
                    continue

                if not item.is_folder:
                    item = None
                    break

                item = item.get_item(tu)
                if item is None:
                    break

                ind += 1

            if item is None:
                #@log_debug('... url do not exist: %s' % url)
                pass

            #log_debug('... return %s' % item)
            return item

    def url(self, prefix=None):
        """
        @param prefix: str the prefix registered in module prefixes, by default prefix='/'
        @return: string , the url of this item from the prefix parent '/' is the root
        """

        if not prefix:
            prefix = URL_SEPARATOR

        # will go through parents until i found a parent that is validated by this prefix
        #log_debug('url - item: %s, prefix: "%s"' % (self, prefix))
        url_list = []
        item = self
        found = False
        while item:
            if prefixes.validate_item_by_prefix(prefix, item, item_caller=self):
                url_list.append(prefix)
                found = True
                break

            url_list.append(item.name)
            item = item.parent

        if found:
            url_list.reverse()
            if url_list and url_list[0] == URL_SEPARATOR:
                url_list[0] = ''

            if len(url_list) == 1 and url_list[0] == '':
                #this is the root item
                url_text = URL_SEPARATOR
            else:
                url_text = URL_SEPARATOR.join(url_list)

        else:
            #log_debug('...getting url of item with prefix "%s" not found' % prefix)
            url_text = ''

        #log_debug('... return: %s' % url_text)
        return url_text

    @staticmethod
    def get_new_sid():
        return uuid.uuid4().hex

    @staticmethod
    def is_string(value, raise_err=False):
        if is_string(value):
            return True
        else:
            if raise_err:
                raise TypeError('expected string type, received %s' % type(value))

            return False

    @staticmethod
    def is_integer(value, raise_err=False):
        if is_integer(value):
            return True
        else:
            if raise_err:
                raise TypeError('expected integer type, received %s' % type(value))

            return False