Beispiel #1
0
class PidLog(db.Model):
    """
    Audit log of actions happening to persistent identifiers.

    This model is primarily used through PersistentIdentifier.log and rarely
    created manually.
    """
    __tablename__ = 'pidLOG'
    __table_args__ = (db.Index('idx_action', 'action'), )

    id = db.Column(db.Integer(15, unsigned=True), primary_key=True)
    """ Id of persistent identifier entry """

    id_pid = db.Column(
        db.Integer(15, unsigned=True),
        db.ForeignKey(PersistentIdentifier.id),
        nullable=True,
    )
    """ PID """

    timestamp = db.Column(db.DateTime(), nullable=False, default=datetime.now)
    """ Creation datetime of entry """

    action = db.Column(db.String(10), nullable=False)
    """ Action identifier """

    message = db.Column(db.Text(), nullable=False)
    """ Log message """
class WebQuery(db.Model):
    """Represents a WebQuery record."""
    __tablename__ = 'query'
    id = db.Column(db.Integer(15, unsigned=True),
                   primary_key=True,
                   autoincrement=True)
    type = db.Column(db.Char(1), nullable=False, server_default='r')
    urlargs = db.Column(db.Text(100), nullable=False, index=True)
def do_upgrade():
    """ Implement your upgrades here  """
    m = db.MetaData(bind=db.engine)
    m.reflect()
    t = db.Table(
        'userCOLLECTION',
        m,
        db.Column('id', db.String(length=100), primary_key=True, nullable=False),
        db.Column('id_user', db.Integer(15, unsigned=True), db.ForeignKey('user.id'), nullable=False),
        db.Column('id_collection', db.MediumInteger(9, unsigned=True), db.ForeignKey('collection.id'), nullable=True),
        db.Column('id_collection_provisional', db.MediumInteger(9, unsigned=True), db.ForeignKey('collection.id'), nullable=True),
        db.Column('id_oairepository', db.MediumInteger(9, unsigned=True), db.ForeignKey('oaiREPOSITORY.id'), nullable=True),
        db.Column('title', db.String(length=255), nullable=False),
        db.Column('description', db.Text(), nullable=False),
        db.Column('page', db.Text(), nullable=False),
        db.Column('curation_policy', db.Text(), nullable=False),
        db.Column('has_logo', db.Boolean(), nullable=False),
        db.Column('created', db.DateTime(), nullable=False),
        db.Column('last_modified', db.DateTime(), nullable=False),
        mysql_engine='MyISAM',
    )
    t.create()
Beispiel #4
0
class KnwKBRVAL(db.Model):
    """Represents a KnwKBRVAL record."""
    def __init__(self):
        pass

    __tablename__ = 'knwKBRVAL'
    id = db.Column(db.MediumInteger(8, unsigned=True),
                   nullable=False,
                   primary_key=True,
                   autoincrement=True)
    m_key = db.Column(db.String(255),
                      nullable=False,
                      server_default='',
                      index=True)
    m_value = db.Column(db.Text(30), nullable=False, index=True)
    id_knwKB = db.Column(db.MediumInteger(8),
                         db.ForeignKey(KnwKB.id),
                         nullable=False,
                         server_default='0')
    kb = db.relationship(KnwKB, backref='kbrvals')
Beispiel #5
0
class UserCollection(db.Model):
    """ Represents a user collection - a layer around
    Invenio's collections and portalboxes, that allow end-users to create
    """
    __tablename__ = 'userCOLLECTION'

    #
    # Fields
    #
    id = db.Column(db.String(100), primary_key=True)
    """ User collection identifier used to generate the real collection_identifier """

    id_user = db.Column(db.Integer(15, unsigned=True),
                        db.ForeignKey(User.id),
                        nullable=False)
    """ Owner of the collection. """

    id_collection = db.Column(db.Integer(15, unsigned=True),
                              db.ForeignKey(Collection.id),
                              nullable=True,
                              default=None)
    """ Invenio collection generated from this collection """

    id_collection_provisional = db.Column(db.Integer(15, unsigned=True),
                                          db.ForeignKey(Collection.id),
                                          nullable=True,
                                          default=None)
    """ Invenio provisional collection generated from this collection """

    id_oairepository = db.Column(db.MediumInteger(9, unsigned=True),
                                 db.ForeignKey(OaiREPOSITORY.id),
                                 nullable=True,
                                 default=None)
    """ OAI Repository set specification """

    title = db.Column(db.String(length=255), nullable=False, default='')
    """ Title of collection. """

    description = db.Column(db.Text(), nullable=False, default='')
    """ Short description of collection, displayed in portal boxes. """

    page = db.Column(db.Text(), nullable=False, default='')
    """ Long description of collection, displayed on an individual page. """

    curation_policy = db.Column(db.Text(), nullable=False, default='')
    """ """

    has_logo = db.Column(db.Boolean(), nullable=False, default=False)
    """ """

    created = db.Column(db.DateTime(), nullable=False, default=datetime.now)
    """ Creation datetime """

    last_modified = db.Column(db.DateTime(),
                              nullable=False,
                              default=datetime.now,
                              onupdate=datetime.now)
    """ Last modification datetime """

    #
    # Relation ships
    #
    owner = db.relationship(User,
                            backref='usercollections',
                            foreign_keys=[id_user])
    """ Relation to the owner (User) of the collection """

    collection = db.relationship(Collection,
                                 uselist=False,
                                 backref='usercollection',
                                 foreign_keys=[id_collection])
    """ Relationship to Invenio collection. """

    collection_provisional = db.relationship(
        Collection,
        uselist=False,
        backref='usercollection_provisional',
        foreign_keys=[id_collection_provisional])
    """ Relationship to Invenio restricted collection containing uncurated records. """

    oai_set = db.relationship(OaiREPOSITORY,
                              uselist=False,
                              backref='usercollection',
                              foreign_keys=[id_oairepository])
    """ Relation to the owner (User) of the collection """

    #
    # Properties
    #
    @property
    def logo_url(self):
        """
        Get URL to collection logo
        """
        # FIXME
        if self.has_logo:
            raise NotImplementedError
        else:
            return None

    @property
    def oai_url(self):
        """ Get link to OAI-PMH API for this user collection """
        return "/oai2d?verb=ListRecords&metadataPrefix=oai_dc&set=%s" % self.get_collection_name(
        )

    @property
    def collection_url(self):
        """ Get URL to collection """
        return "/collection/%s" % self.get_collection_name()

    @property
    def collection_provisional_url(self):
        """ Get URL to provisional collection """
        return "/search?cc=%s" % self.get_collection_name(provisional=True)

    @property
    def upload_url(self):
        """ Get direct upload URL """
        return url_for('webdeposit.index', c=self.id)

    #
    #
    #
    @classmethod
    def from_recid(cls, recid, provisional=False):
        """ Get user collections specified in recid """
        rec = get_record(recid)
        prefix = "%s-" % (CFG_USERCOLLECTION_ID_PREFIX_PROVISIONAL
                          if provisional else CFG_USERCOLLECTION_ID_PREFIX)

        colls = rec.get('980', [])
        usercolls = []
        for c in colls:
            try:
                # We are only interested in subfield 'a'
                code, val = c[0][0]
                if code == 'a' and val.startswith(prefix):
                    val = val[len(prefix):]
                    u = UserCollection.query.filter_by(id=val).first()
                    if u:
                        usercolls.append(u)
            except IndexError:
                pass
        return usercolls

    #
    # Utility methods
    #
    def get_collection_name(self, provisional=False):
        """ Get a unique collection name identifier """
        if provisional:
            return "%s-%s" % (CFG_USERCOLLECTION_ID_PREFIX_PROVISIONAL,
                              self.id)
        else:
            return "%s-%s" % (CFG_USERCOLLECTION_ID_PREFIX, self.id)

    def get_title(self, provisional=False):
        if provisional:
            return "Provisional: %s" % self.title
        else:
            return self.title

    def get_collection_dbquery(self, provisional=False):
        """ Get collection query """
        return "%s:%s" % self.get_query(provisional=provisional)

    def get_query(self, provisional=False):
        """ Get tuple (field,value) for search engine query """
        return ("980__a", self.get_collection_name(provisional=provisional))

    def render_portalbox_bodies(self, templates):
        """
        Get a list of rendered portal boxes for this user collection
        """
        ctx = {
            'usercollection': self,
        }

        return map(lambda t: render_template_to_string(t, **ctx), templates)

    #
    # Curation methods
    #
    def _modify_record(self,
                       recid,
                       test_func,
                       replace_func,
                       include_func,
                       append_colls=[],
                       replace_colls=[]):
        """
        Generate record a MARCXML file

        @param test_func: Function to test if a collection id should be changed
        @param replace_func: Function to replace the collection id.
        @param include_func: Function to test if collection should be included
        """
        rec = get_record(recid)
        newcolls = []
        dirty = False

        try:
            colls = rec['980']
            if replace_colls:
                for c in replace_colls:
                    newcolls.append([('a', c)])
                    dirty = True
            else:
                for c in colls:
                    try:
                        # We are only interested in subfield 'a'
                        code, val = c[0][0]
                        if test_func(code, val):
                            c[0][0] = replace_func(code, val)
                            dirty = True
                        if include_func(code, val):
                            newcolls.append(c[0])
                        else:
                            dirty = True
                    except IndexError:
                        pass
                for c in append_colls:
                    newcolls.append([('a', c)])
                    dirty = True
        except KeyError:
            return False

        if not dirty:
            return False

        rec = {}
        record_add_field(rec, '001', controlfield_value=str(recid))

        for subfields in newcolls:
            record_add_field(rec, '980', subfields=subfields)

        return rec

    def _upload_record(self, rec, pretend=False):
        """
        Bibupload one record
        """
        if rec is False:
            return None
        if not pretend:
            bibupload_record(
                record=rec,
                file_prefix='usercoll',
                mode='-c',
                opts=['-n', '-P5'],
                alias="usercoll",
            )
        return rec

    def _upload_collection(self, coll):
        """
        Bibupload many records
        """
        bibupload_record(
            collection=coll,
            file_prefix='usercoll',
            mode='-c',
            opts=['-n', '-P5'],
            alias="usercoll",
        )
        return True

    def accept_record(self, recid, pretend=False):
        """
        Accept a record for inclusion in a user collection

        @param recid: Record ID
        """
        expected_id = self.get_collection_name(provisional=True)
        new_id = self.get_collection_name(provisional=False)

        append_colls, replace_colls = signalresult2list(
            pre_curation.send(self,
                              action='accept',
                              recid=recid,
                              pretend=pretend))

        def test_func(code, val):
            return code == 'a' and val == expected_id

        def replace_func(code, val):
            return (code, new_id)

        def include_func(code, val):
            return True

        rec = self._upload_record(self._modify_record(
            recid,
            test_func,
            replace_func,
            include_func,
            append_colls=append_colls,
            replace_colls=replace_colls),
                                  pretend=pretend)

        post_curation.send(self,
                           action='accept',
                           recid=recid,
                           record=rec,
                           pretend=pretend)
        return rec

    def reject_record(self, recid, pretend=False):
        """
        Reject a record for inclusion in a user collection

        @param recid: Record ID
        """
        expected_id = self.get_collection_name(provisional=True)
        new_id = self.get_collection_name(provisional=False)

        append_colls, replace_colls = signalresult2list(
            pre_curation.send(self,
                              action='reject',
                              recid=recid,
                              pretend=pretend))

        def test_func(code, val):
            return False

        def replace_func(code, val):
            return (code, val)

        def include_func(code, val):
            return not (code == 'a' and (val == expected_id or val == new_id))

        rec = self._upload_record(self._modify_record(
            recid,
            test_func,
            replace_func,
            include_func,
            append_colls=append_colls,
            replace_colls=replace_colls),
                                  pretend=pretend)

        post_curation.send(self,
                           action='reject',
                           recid=recid,
                           record=rec,
                           pretend=pretend)
        return rec

    #
    # Data persistence methods
    #
    def save_collectionname(self, collection, title):
        """
        Create or update Collectionname object
        """
        if collection.id:
            c_name = Collectionname.query.filter_by(
                id_collection=collection.id, ln=CFG_SITE_LANG,
                type='ln').first()
            if c_name:
                update_changed_fields(c_name, dict(value=title))
                return c_name

        c_name = Collectionname(
            collection=collection,
            ln=CFG_SITE_LANG,
            type='ln',
            value=title,
        )
        db.session.add(c_name)
        return c_name

    def save_collectiondetailedrecordpagetabs(self, collection):
        """
        Create or update Collectiondetailedrecordpagetabs object
        """
        if collection.id:
            c_tabs = Collectiondetailedrecordpagetabs.query.filter_by(
                id_collection=collection.id).first()
            if c_tabs:
                update_changed_fields(c_tabs,
                                      dict(tabs=CFG_USERCOLLCTION_TABS))
                return c_tabs

        c_tabs = Collectiondetailedrecordpagetabs(
            collection=collection,
            tabs=CFG_USERCOLLCTION_TABS,
        )
        db.session.add(c_tabs)
        return c_tabs

    def save_collectioncollection(self, collection, parent_name):
        """
        Create or update CollectionCollection object
        """
        dad = Collection.query.filter_by(name=parent_name).first()

        if collection.id:
            c_tree = CollectionCollection.query.filter_by(
                id_dad=dad.id, id_son=collection.id).first()
            if c_tree:
                update_changed_fields(
                    c_tree,
                    dict(type=CFG_USERCOLLECTION_COLLECTION_TYPE,
                         score=CFG_USERCOLLECTION_COLLECTION_SCORE))
                return c_tree

        c_tree = CollectionCollection(
            dad=dad,
            son=collection,
            type=CFG_USERCOLLECTION_COLLECTION_TYPE,
            score=CFG_USERCOLLECTION_COLLECTION_SCORE,
        )
        db.session.add(c_tree)
        return c_tree

    def save_collectionformat(self, collection, fmt_str):
        """
        Create or update CollectionFormat object
        """
        fmt = Format.query.filter_by(code=fmt_str).first()

        if collection.id:
            c_fmt = CollectionFormat.query.filter_by(
                id_collection=collection.id).first()
            if c_fmt:
                update_changed_fields(c_fmt, dict(id_format=fmt.id, score=1))
                return c_fmt

        c_fmt = CollectionFormat(
            collection=collection,
            format=fmt,
        )
        db.session.add(c_fmt)
        return c_fmt

    def save_collectionportalboxes(self, collection, templates):
        """
        Create or update Portalbox and CollectionPortalbox objects
        """
        # Setup portal boxes
        bodies = self.render_portalbox_bodies(templates)
        bodies.reverse()  # Highest score is on the top, so we reverse the list

        objects = []
        if collection.id:
            c_pboxes = CollectionPortalbox.query.filter_by(
                id_collection=collection.id,
                ln=CFG_SITE_LANG,
            ).all()
            if len(c_pboxes) == len(bodies):
                for score, elem in enumerate(zip(c_pboxes, bodies)):
                    c_pbox, body = elem
                    pbox = c_pbox.portalbox
                    update_changed_fields(pbox, dict(body=body))
                    update_changed_fields(
                        c_pbox,
                        dict(score=score,
                             position=CFG_USERCOLLECTION_PORTALBOX_POSITION))
                    objects.append(c_pbox)
                return objects
            else:
                # Either templates where modified or collection portalboxes
                # where modified outside of the UserCollection. In either case,
                # remove existing portalboxes and add new ones.
                for c_pbox in c_pboxes:
                    db.session.delete(c_pbox.portalbox)
                    db.session.delete(c_pbox)

        for score, body in enumerate(bodies):
            p = Portalbox(title='', body=body)
            c_pbox = CollectionPortalbox()
            update_changed_fields(
                c_pbox,
                dict(
                    collection=collection,
                    portalbox=p,
                    ln=CFG_SITE_LANG,
                    position=CFG_USERCOLLECTION_PORTALBOX_POSITION,
                    score=score,
                ))
            db.session.add_all([p, c_pbox])
            objects.append(c_pbox)
        return objects

    def save_oairepository_set(self, provisional=False):
        """
        Create or update OAI Repository set.
        """
        collection_name = self.get_collection_name(provisional=provisional)
        (f1, p1) = self.get_query(provisional=provisional)
        fields = dict(
            setName='%s set' % collection_name,
            setSpec=collection_name,
            setDescription=self.description,
            p1=p1,
            f1=f1,
            m1='e',
            p2='',
            f2='',
            m2='',
            p3='',
            f3='',
            m3='',
        )

        if self.oai_set:
            update_changed_fields(self.oai_set, fields)
        else:
            self.oai_set = OaiREPOSITORY(**fields)
            db.session.add(self.oai_set)

    def save_acl(self, collection_id, collection_name):
        """
        Create or update authorization for user to view the provisional collection
        """
        # Role - use collection id, because role name is limited to 32 chars.
        role_name = 'coll_%s' % collection_id
        role = AccROLE.query.filter_by(name=role_name).first()
        if not role:
            role = AccROLE(name=role_name,
                           description='Curators of collection %s' %
                           collection_name)
            db.session.add(role)

        # Argument
        fields = dict(keyword='collection', value=collection_name)
        arg = AccARGUMENT.query.filter_by(**fields).first()
        if not arg:
            arg = AccARGUMENT(**fields)
            db.session.add(arg)

        # Action
        action = AccACTION.query.filter_by(name='viewrestrcoll').first()

        # User role
        alluserroles = UserAccROLE.query.filter_by(role=role).all()
        userrole = None
        if alluserroles:
            # Remove any user which is not the owner
            for ur in alluserroles:
                if ur.id_user == self.id_user:
                    db.session.delete(ur)
                else:
                    userrole = ur

        if not userrole:
            userrole = UserAccROLE(user=self.owner, role=role)
            db.session.add(userrole)

        # Authorization
        auth = AccAuthorization.query.filter_by(role=role,
                                                action=action,
                                                argument=arg).first()
        if not auth:
            auth = AccAuthorization(role=role,
                                    action=action,
                                    argument=arg,
                                    argumentlistid=1)

    def save_collection(self, provisional=False):
        """
        Create or update a new collection with name, tabs, collection tree,
        collection output formats, portalboxes and OAI repository set
        """
        # Setup collection
        collection_name = self.get_collection_name(provisional=provisional)
        c = Collection.query.filter_by(name=collection_name).first()
        fields = dict(
            name=collection_name,
            dbquery=self.get_collection_dbquery(provisional=provisional))

        if c:
            before_save_collection.send(self,
                                        is_new=True,
                                        provisional=provisional)
            update_changed_fields(c, fields)
        else:
            before_save_collection.send(self,
                                        is_new=False,
                                        provisional=provisional)
            c = Collection(**fields)
            db.session.add(c)
            db.session.commit()
        setattr(self,
                'collection_provisional' if provisional else 'collection', c)

        # Setup OAI Repository
        if provisional:
            self.save_acl(c.id, collection_name)
        else:
            self.save_oairepository_set(provisional=provisional)

        # Setup title, tabs and collection tree
        self.save_collectionname(c, self.get_title(provisional=provisional))
        self.save_collectiondetailedrecordpagetabs(c)
        self.save_collectioncollection(
            c, CFG_USERCOLLCTION_PARENT_NAME_PROVISIONAL
            if provisional else CFG_USERCOLLCTION_PARENT_NAME)

        # Setup collection format is needed
        if not provisional and CFG_USERCOLLCTION_OUTPUTFORMAT:
            self.save_collectionformat(c, CFG_USERCOLLCTION_OUTPUTFORMAT)
        elif provisional and CFG_USERCOLLCTION_OUTPUTFORMAT_PROVISIONAL:
            self.save_collectionformat(
                c, CFG_USERCOLLCTION_OUTPUTFORMAT_PROVISIONAL)

        # Setup portal boxes
        self.save_collectionportalboxes(
            c, CFG_USERCOLLCTION_PORTALBOXES_PROVISIONAL
            if provisional else CFG_USERCOLLCTION_PORTALBOXES)
        db.session.commit()
        after_save_collection.send(self, collection=c, provisional=provisional)

    def save_collections(self):
        """
        Create restricted and unrestricted collection
        """
        before_save_collections.send(self)
        self.save_collection(provisional=False)
        self.save_collection(provisional=True)
        after_save_collections.send(self)

    def delete_record_collection_identifiers(self):
        """
        Remove collection identifiers for this user collection from all records.
        """
        provisional_id = self.get_collection_name(provisional=True)
        normal_id = self.get_collection_name(provisional=False)

        def test_func(code, val):
            return False

        def replace_func(code, val):
            return (code, val)

        def include_func(code, val):
            return not (code == 'a' and
                        (val == provisional_id or val == normal_id))

        coll = []
        for r in search_pattern(p="980__a:%s OR 980__a:%s" %
                                (normal_id, provisional_id)):
            coll.append(
                self._modify_record(r, test_func, replace_func, include_func))

        self._upload_collection(coll)

    def delete_collection(self, provisional=False):
        """
        Delete all objects related to a single collection
        """
        # Most of the logic in this method ought to be moved to a
        # Collection.delete() method.
        c = getattr(self,
                    "collection_provisional" if provisional else "collection")
        collction_name = self.get_collection_name(provisional=provisional)

        before_delete_collection.send(self,
                                      collection=c,
                                      provisional=provisional)

        if c:
            # Delete portal boxes
            for c_pbox in c.portalboxes:
                if c_pbox.portalbox:
                    db.session.delete(c_pbox.portalbox)
                db.session.delete(c_pbox)

            # Delete output formats:
            CollectionFormat.query.filter_by(id_collection=c.id).delete()

            # Delete title, tabs, collection tree
            Collectionname.query.filter_by(id_collection=c.id).delete()
            CollectionCollection.query.filter_by(id_son=c.id).delete()
            Collectiondetailedrecordpagetabs.query.filter_by(
                id_collection=c.id).delete()

        if provisional:
            # Delete ACLs
            AccARGUMENT.query.filter_by(keyword='collection',
                                        value=collction_name).delete()
            role = AccROLE.query.filter_by(name='coll_%s' % c.id).first()
            if role:
                UserAccROLE.query.filter_by(role=role).delete()
                AccAuthorization.query.filter_by(role=role).delete()
                db.session.delete(role)
        else:
            # Delete OAI repository
            if self.oai_set:
                db.session.delete(self.oai_set)

        # Delete collection
        if c:
            db.session.delete(c)
        db.session.commit()
        after_delete_collection.send(self, provisional=provisional)

    def delete_collections(self):
        """
        Delete collection and all associated objects.
        """
        before_delete_collections.send(self)
        self.delete_record_collection_identifiers()
        self.delete_collection(provisional=False)
        self.delete_collection(provisional=True)
        after_delete_collections.send(self)
class Collection(db.Model):
    """Represents a Collection record."""
    def __repr__(self):
        return "%s(%s)" % (self.__class__.__name__, self.id)

    __tablename__ = 'collection'
    id = db.Column(db.MediumInteger(9, unsigned=True), primary_key=True)
    name = db.Column(db.String(255), unique=True, index=True, nullable=False)
    dbquery = db.Column(db.Text(20), nullable=True, index=True)
    nbrecs = db.Column(db.Integer(10, unsigned=True), server_default='0')
    #FIXME read only!!!
    reclist = db.Column(
        db.PickleType(pickler=IntbitsetPickle(), comparator=IntbitsetCmp))
    _names = db.relationship(
        lambda: Collectionname,
        backref='collection',
        collection_class=attribute_mapped_collection('ln_type'),
        cascade="all, delete, delete-orphan")

    names = association_proxy(
        '_names',
        'value',
        creator=lambda k, v: Collectionname(ln_type=k, value=v))

    _formatoptions = association_proxy('formats', 'format')

    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def formatoptions(self):
        if len(self._formatoptions):
            return [dict(f) for f in self._formatoptions]
        else:
            return [{
                'code': 'hb',
                'name': "HTML %s" % g._("brief"),
                'content_type': 'text/html'
            }]

    formatoptions = property(formatoptions)

    _examples_example = association_proxy('_examples', 'example')

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def examples(self):
        return list(self._examples_example)

    @property
    def name_ln(self):
        from invenio.search_engine import get_coll_i18nname
        return get_coll_i18nname(self.name, g.ln).decode('utf-8')
        # Another possible implementation with cache memoize
        # @cache.memoize
        #try:
        #    return db.object_session(self).query(Collectionname).\
        #        with_parent(self).filter(db.and_(Collectionname.ln==g.ln,
        #            Collectionname.type=='ln')).first().value
        #except:
        #    return self.name

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def portalboxes_ln(self):
        return db.object_session(self).query(CollectionPortalbox).\
            with_parent(self).\
            options(db.joinedload_all(CollectionPortalbox.portalbox)).\
            filter(CollectionPortalbox.ln == g.ln).\
            order_by(db.desc(CollectionPortalbox.score)).all()

    @property
    def most_specific_dad(self):
        return db.object_session(self).query(Collection).\
            join(Collection.sons).\
            filter(CollectionCollection.id_son == self.id).\
            order_by(db.asc(Collection.nbrecs)).\
            first()

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def is_restricted(self):
        from invenio.search_engine import collection_restricted_p
        return collection_restricted_p(self.name)

    @property
    def type(self):
        p = re.compile("\d+:.*")
        if self.dbquery is not None and \
            p.match(self.dbquery.lower()):
            return 'r'
        else:
            return 'v'

    _collection_children = db.relationship(
        lambda: CollectionCollection,
        #collection_class=OrderedList,
        collection_class=ordering_list('score'),
        primaryjoin=lambda: Collection.id == CollectionCollection.id_dad,
        foreign_keys=lambda: CollectionCollection.id_dad,
        order_by=lambda: db.asc(CollectionCollection.score))
    _collection_children_r = db.relationship(
        lambda: CollectionCollection,
        #collection_class=OrderedList,
        collection_class=ordering_list('score'),
        primaryjoin=lambda: db.and_(
            Collection.id == CollectionCollection.id_dad, CollectionCollection.
            type == 'r'),
        foreign_keys=lambda: CollectionCollection.id_dad,
        order_by=lambda: db.asc(CollectionCollection.score))
    _collection_children_v = db.relationship(
        lambda: CollectionCollection,
        #collection_class=OrderedList,
        collection_class=ordering_list('score'),
        primaryjoin=lambda: db.and_(
            Collection.id == CollectionCollection.id_dad, CollectionCollection.
            type == 'v'),
        foreign_keys=lambda: CollectionCollection.id_dad,
        order_by=lambda: db.asc(CollectionCollection.score))
    collection_parents = db.relationship(
        lambda: CollectionCollection,
        #collection_class=OrderedList,
        collection_class=ordering_list('score'),
        primaryjoin=lambda: Collection.id == CollectionCollection.id_son,
        foreign_keys=lambda: CollectionCollection.id_son,
        order_by=lambda: db.asc(CollectionCollection.score))
    collection_children = association_proxy('_collection_children', 'son')
    collection_children_r = association_proxy(
        '_collection_children_r',
        'son',
        creator=lambda son: CollectionCollection(id_son=son.id, type='r'))
    collection_children_v = association_proxy(
        '_collection_children_v',
        'son',
        creator=lambda son: CollectionCollection(id_son=son.id, type='v'))

    #
    _externalcollections = db.relationship(
        lambda: CollectionExternalcollection,
        #            backref='collection',
        cascade="all, delete, delete-orphan")

    #
    #    externalcollections = association_proxy(
    #        '_externalcollections',
    #        'externalcollection')

    def _externalcollections_type(type):
        return association_proxy(
            '_externalcollections_' + str(type),
            'externalcollection',
            creator=lambda ext: CollectionExternalcollection(
                externalcollection=ext, type=type))

    externalcollections_0 = _externalcollections_type(0)
    externalcollections_1 = _externalcollections_type(1)
    externalcollections_2 = _externalcollections_type(2)

    externalcollections = db.relationship(
        lambda: CollectionExternalcollection,
        #backref='collection',
        collection_class=external_collection_mapper,
        cascade="all, delete, delete-orphan")

    # Search options
    _make_field_fieldvalue = lambda type: db.relationship(
        lambda: CollectionFieldFieldvalue,
        primaryjoin=lambda: db.and_(
            Collection.id == CollectionFieldFieldvalue.id_collection,
            CollectionFieldFieldvalue.type == type),
        order_by=lambda: CollectionFieldFieldvalue.score)

    _search_within = _make_field_fieldvalue('sew')
    _search_options = _make_field_fieldvalue('seo')

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def search_within(self):
        """
        Collect search within options.
        """
        from invenio.search_engine_config import CFG_WEBSEARCH_SEARCH_WITHIN
        default = [('', g._('any field'))]
        found = [(o.field.code, o.field.name_ln) for o in self._search_within]
        if not found:
            found = [(f.name.replace(' ', ''), f.name_ln)
                     for f in Field.query.filter(
                         Field.name.in_(CFG_WEBSEARCH_SEARCH_WITHIN)).all()]
        return default + sorted(found, key=itemgetter(1))

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def search_options(self):
        return self._search_options

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def ancestors_ids(self):
        """Get list of parent collection ids."""
        output = intbitset([self.id])
        for c in self.dads:
            ancestors = c.dad.ancestors_ids
            if self.id in ancestors:
                raise
            output |= ancestors
        return output

    @property
    #@cache.memoize(make_name=lambda fname: fname + '::' + g.ln)
    def descendants_ids(self):
        """Get list of child collection ids."""
        output = intbitset([self.id])
        for c in self.sons:
            descendants = c.son.descendants_ids
            if self.id in descendants:
                raise
            output |= descendants
        return output

    # Gets the list of localized names as an array
    collection_names = db.relationship(
        lambda: Collectionname,
        primaryjoin=lambda: Collection.id == Collectionname.id_collection,
        foreign_keys=lambda: Collectionname.id_collection)

    # Gets the translation according to the lang code
    def translation(self, lang):
        try:
            return db.object_session(self).query(Collectionname).\
                with_parent(self).filter(db.and_(Collectionname.ln == lang,
                    Collectionname.type == 'ln')).first().value
        except:
            return ""

    portal_boxes_ln = db.relationship(
            lambda: CollectionPortalbox,
            #collection_class=OrderedList,
            collection_class=ordering_list('score'),
            primaryjoin=lambda: \
                Collection.id == CollectionPortalbox.id_collection,
            foreign_keys=lambda: CollectionPortalbox.id_collection,
            order_by=lambda: db.asc(CollectionPortalbox.score))

    #@db.hybrid_property
    #def externalcollections(self):
    #    return self._externalcollections

    #@externalcollections.setter
    #def externalcollections(self, data):
    #    if isinstance(data, dict):
    #        for k, vals in data.iteritems():
    #            for v in list(vals):
    #                self._externalcollections[k] = v
    #    else:
    #        self._externalcollections = data

    def breadcrumbs(self, builder=None, ln=CFG_SITE_LANG):
        """Retunds breadcrumbs for collection."""
        breadcrumbs = []
        # Get breadcrumbs for most specific dad if it exists.
        if self.most_specific_dad is not None:
            breadcrumbs = self.most_specific_dad.breadcrumbs(builder=builder,
                                                             ln=ln)

        if builder is not None:
            crumb = builder(self)
        else:
            crumb = (self.name_ln, 'search.collection', dict(name=self.name))
        breadcrumbs.append(crumb)
        return breadcrumbs