Пример #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."""

    # Relationship
    pid = db.relationship("PersistentIdentifier", backref="logs")
Пример #2
0
class AccARGUMENT(db.Model):

    """Represent an authorization argument."""

    __tablename__ = 'accARGUMENT'
    id = db.Column(db.Integer(15), primary_key=True, autoincrement=True)
    keyword = db.Column(db.String(32), nullable=True)
    value = db.Column(db.String(255), nullable=True)
    __table_args__ = (db.Index('KEYVAL', keyword, value),
                      db.Model.__table_args__)

    def __repr__(self):
        """Repr."""
        return "{0.keyword}={0.value}".format(self)
class UserEXT(db.Model):
    """Represent a UserEXT record."""

    __tablename__ = 'userEXT'

    id = db.Column(db.String(255), primary_key=True, nullable=False)
    method = db.Column(db.String(50), primary_key=True, nullable=False)
    id_user = db.Column(db.Integer(15, unsigned=True),
                        db.ForeignKey(User.id),
                        nullable=False)

    user = db.relationship(User, backref="external_identifiers")

    __table_args__ = (db.Index('userext_id_user_method',
                               id_user,
                               method,
                               unique=True), db.Model.__table_args__)
Пример #4
0
class PersistentIdentifier(db.Model):

    """Store and register persistent identifiers.

    Assumptions:
      * Persistent identifiers can be represented as a string of max 255 chars.
      * An object has many persistent identifiers.
      * A persistent identifier has one and only one object.
    """

    __tablename__ = 'pidSTORE'
    __table_args__ = (
        db.Index('uidx_type_pid', 'pid_type', 'pid_value', unique=True),
        db.Index('idx_status', 'status'),
        db.Index('idx_object', 'object_type', 'object_value'),
    )

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

    pid_type = db.Column(db.String(6), nullable=False)
    """Persistent Identifier Schema."""

    pid_value = db.Column(db.String(length=255), nullable=False)
    """Persistent Identifier."""

    pid_provider = db.Column(db.String(length=255), nullable=False)
    """Persistent Identifier Provider"""

    status = db.Column(db.CHAR(length=1), nullable=False)
    """Status of persistent identifier, e.g. registered, reserved, deleted."""

    object_type = db.Column(db.String(3), nullable=True)
    """Object Type - e.g. rec for record."""

    object_value = db.Column(db.String(length=255), nullable=True)
    """Object ID - e.g. a record id."""

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

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

    #
    # Class methods
    #
    @classmethod
    def create(cls, pid_type, pid_value, pid_provider='', provider=None):
        """Internally reserve a new persistent identifier.

        A provider for the given persistent identifier type must exists. By
        default the system will choose a provider according to the pid
        type. If desired, the default system provider can be overridden via
        the provider keyword argument.

        Return PID object if successful otherwise None.
        """
        # Ensure provider exists
        if provider is None:
            provider = PidProvider.create(pid_type, pid_value, pid_provider)
            if not provider:
                raise Exception(
                    "No provider found for %s:%s (%s)" % (
                        pid_type, pid_value, pid_provider)
                )

        db.session.begin_nested()
        try:
            obj = cls(pid_type=provider.pid_type,
                      pid_value=provider.create_new_pid(pid_value),
                      pid_provider=pid_provider,
                      status=cfg['PIDSTORE_STATUS_NEW'])
            obj._provider = provider
            db.session.add(obj)
            obj.log("CREATE", "Created")
            return obj
        except SQLAlchemyError:
            db.session.rollback()
            obj.log("CREATE", "Failed to created. Already exists.")
            return None

    @classmethod
    def get(cls, pid_type, pid_value, pid_provider='', provider=None):
        """Get persistent identifier.

        Return None if not found.
        """
        pid_value = to_unicode(pid_value)
        obj = cls.query.filter_by(
            pid_type=pid_type, pid_value=pid_value, pid_provider=pid_provider
        ).first()
        if obj:
            obj._provider = provider
            return obj
        else:
            return None

    #
    # Instance methods
    #
    def has_object(self, object_type, object_value):
        """Determine if this PID is assigned to a specific object."""
        if object_type not in cfg['PIDSTORE_OBJECT_TYPES']:
            raise Exception("Invalid object type %s." % object_type)

        object_value = to_unicode(object_value)

        return self.object_type == object_type and \
            self.object_value == object_value

    def get_provider(self):
        """Get the provider for this type of persistent identifier."""
        if self._provider is None:
            self._provider = PidProvider.create(
                self.pid_type, self.pid_value, self.pid_provider
            )
        return self._provider

    def assign(self, object_type, object_value, overwrite=False):
        """Assign this persistent identifier to a given object.

        Note, the persistent identifier must first have been reserved. Also,
        if an exsiting object is already assigned to the pid, it will raise an
        exception unless overwrite=True.
        """
        if object_type not in cfg['PIDSTORE_OBJECT_TYPES']:
            raise Exception("Invalid object type %s." % object_type)
        object_value = to_unicode(object_value)

        if not self.id:
            raise Exception(
                "You must first create the persistent identifier before you "
                "can assign objects to it."
            )

        if self.is_deleted():
            raise Exception(
                "You cannot assign objects to a deleted persistent identifier."
            )

        with db.session.begin_nested():
            # Check for an existing object assigned to this pid
            existing_obj_id = self.get_assigned_object(object_type)

            if existing_obj_id and existing_obj_id != object_value:
                if not overwrite:
                    raise Exception(
                        "Persistent identifier is already assigned to another "
                        "object"
                    )
                else:
                    self.log(
                        "ASSIGN",
                        "Unassigned object %s:%s (overwrite requested)" % (
                            self.object_type, self.object_value)
                    )
                    self.object_type = None
                    self.object_value = None
            elif existing_obj_id and existing_obj_id == object_value:
                # The object is already assigned to this pid.
                return True

            self.object_type = object_type
            self.object_value = object_value
            db.session.commit()
            self.log("ASSIGN", "Assigned object {0}:{1}".format(
                self.object_type, self.object_value
            ))
            return True

    def update(self, with_deleted=False, *args, **kwargs):
        """Update the persistent identifier with the provider."""
        if self.is_new() or self.is_reserved():
            raise Exception(
                "Persistent identifier has not yet been registered."
            )

        if not with_deleted and self.is_deleted():
            raise Exception("Persistent identifier has been deleted.")

        with db.session.begin_nested():
            provider = self.get_provider()
            if provider is None:
                self.log("UPDATE", "No provider found.")
                raise Exception("No provider found.")

            if provider.update(self, *args, **kwargs):
                if with_deleted and self.is_deleted():
                    self.status = cfg['PIDSTORE_ST):TUS_REGISTERED']
                return True
        return False

    def reserve(self, *args, **kwargs):
        """Reserve the persistent identifier with the provider.

        Note, the reserve method may be called multiple times, even if it was
        already reserved.
        """
        if not (self.is_new() or self.is_reserved()):
            raise Exception(
                "Persistent identifier has already been registered."
            )

        with db.session.begin_nested():
            provider = self.get_provider()
            if provider is None:
                self.log("RESERVE", "No provider found.")
                raise Exception("No provider found.")

            if provider.reserve(self, *args, **kwargs):
                self.status = cfg['PIDSTORE_STATUS_RESERVED']
                return True
        return False

    def register(self, *args, **kwargs):
        """Register the persistent identifier with the provider."""
        if self.is_registered() or self.is_deleted():
            raise Exception(
                "Persistent identifier has already been registered."
            )

        with db.session.begin_nested():
            provider = self.get_provider()
            if provider is None:
                self.log("REGISTER", "No provider found.")
                raise Exception("No provider found.")

            if provider.register(self, *args, **kwargs):
                self.status = cfg['PIDSTORE_STATUS_REGISTERED']
                return True
        return False

    def delete(self, *args, **kwargs):
        """Delete the persistent identifier."""
        with db.session.begin_nested():
            if self.is_new():
                # New persistent identifier which haven't been registered yet.
                # Just delete it completely but keep log)
                # Remove links to log entries (leave the otherwise)
                PidLog.query.filter_by(id_pid=self.id).update({'id_pid': None})
                db.session.delete(self)
                self.log("DELETE", "Unregistered PID successfully deleted")
            else:
                provider = self.get_provider()
                if not provider.delete(self, *args, **kwargs):
                    return False
                self.status = cfg['PIDSTORE_STATUS_DELETED']
            return True

    def sync_status(self, *args, **kwargs):
        """Synchronize persistent identifier status.

        Used when the provider uses an external service, which might have been
        modified outside of our system.
        """
        with db.session.begin_nested():
            provider = self.get_provider()
            result = provider.sync_status(self, *args, **kwargs)
            return result

    def get_assigned_object(self, object_type=None):
        """Return an assigned object."""
        if object_type is not None and self.object_type == object_type:
            return self.object_value
        return None

    def is_registered(self):
        """Return true if the persistent identifier has been registered."""
        return self.status == cfg['PIDSTORE_STATUS_REGISTERED']

    def is_deleted(self):
        """Return true if the persistent identifier has been deleted."""
        return self.status == cfg['PIDSTORE_STATUS_DELETED']

    def is_new(self):
        """Return true if the PIDhas not yet been registered or reserved."""
        return self.status == cfg['PIDSTORE_STATUS_NEW']

    def is_reserved(self):
        """Return true if the PID has not yet been reserved."""
        return self.status == cfg['PIDSTORE_STATUS_RESERVED']

    def log(self, action, message):
        """Store action and message in log."""
        if self.pid_type and self.pid_value:
            message = "[%s:%s] %s" % (self.pid_type, self.pid_value, message)
        with db.session.begin_nested():
            p = PidLog(id_pid=self.id, action=action, message=message)
            db.session.add(p)
Пример #5
0
class HstEXCEPTION(db.Model):
    """Represents a HstEXCEPTION record."""
    __tablename__ = 'hstEXCEPTION'
    id = db.Column(db.Integer(15, unsigned=True),
                   nullable=False,
                   primary_key=True,
                   autoincrement=True)
    name = db.Column(db.String(50), nullable=False)
    filename = db.Column(db.String(255), nullable=True)
    line = db.Column(db.Integer(9), nullable=True)
    last_seen = db.Column(db.DateTime,
                          nullable=False,
                          server_default='1900-01-01 00:00:00',
                          index=True)
    last_notified = db.Column(db.DateTime,
                              nullable=False,
                              server_default='1900-01-01 00:00:00',
                              index=True)
    counter = db.Column(db.Integer(15), nullable=False, server_default='0')
    total = db.Column(db.Integer(15),
                      nullable=False,
                      server_default='0',
                      index=True)

    __table_args__ = (db.Index('name', name, filename, line,
                               unique=True), db.Model.__table_args__)

    @classmethod
    def get_or_create(cls, name, filename, line):
        """Finds or create exception log."""
        try:
            log = cls.query.filter_by(name=name, filename=filename,
                                      line=line).one()
            delta = datetime.datetime.now() - log.last_notified
            reset_counter = (delta.seconds + delta.days * 86400) >= \
                cfg['CFG_ERRORLIB_RESET_EXCEPTION_NOTIFICATION_COUNTER_AFTER']
            counter = 1 if reset_counter else log.counter + 1
            log.update(
                {
                    'last_notified': db.func.now(),
                    'counter': counter,
                    'total': log.total + 1
                },
                synchronize_settion=False)
            db.session.add(log)
        except:
            log = HstEXCEPTION(name=name,
                               filename=filename,
                               line=line,
                               last_seen=datetime.now(),
                               last_notified=datetime.now(),
                               counter=1,
                               total=1)
            db.session.add(log)
        try:
            db.session.commit()
        except:
            db.session.rollback()
        return log

    @property
    def exception_should_be_notified(self):
        return _is_pow_of_2(self.counter)

    @property
    def pretty_notification_info(self):
        return ("This exception has already been seen %s times\n    "
                "last time it was seen: %s\n    "
                "last time it was notified: %s\n" %
                (self.total, self.last_seen.strftime("%Y-%m-%d %H:%M:%S"),
                 self.last_notified.strftime("%Y-%m-%d %H:%M:%S")))

    @classmethod
    def get_pretty_notification_info(cls, name, filename, line):
        """
        Return a sentence describing when this exception was already seen.
        """
        try:
            return cls.query.filter_by(
                name=name, filename=filename,
                line=line).one().pretty_notification_info
        except:
            return "It is the first time this exception has been seen.\n"
class CmtRECORDCOMMENT(db.Model):
    """Represents a CmtRECORDCOMMENT record."""

    __tablename__ = 'cmtRECORDCOMMENT'

    id = db.Column(db.Integer(15, unsigned=True),
                   nullable=False,
                   primary_key=True,
                   autoincrement=True)
    id_bibrec = db.Column(db.MediumInteger(8, unsigned=True),
                          db.ForeignKey(Bibrec.id),
                          nullable=False,
                          server_default='0')
    id_user = db.Column(db.Integer(15, unsigned=True),
                        db.ForeignKey(User.id),
                        nullable=False,
                        server_default='0')
    title = db.Column(db.String(255), nullable=False, server_default='')
    body = db.Column(db.Text, nullable=False)
    date_creation = db.Column(db.DateTime,
                              nullable=False,
                              server_default='1900-01-01 00:00:00')
    star_score = db.Column(db.TinyInteger(5, unsigned=True),
                           nullable=False,
                           server_default='0')
    nb_votes_yes = db.Column(db.Integer(10),
                             nullable=False,
                             server_default='0')
    nb_votes_total = db.Column(db.Integer(10, unsigned=True),
                               nullable=False,
                               server_default='0')
    nb_abuse_reports = db.Column(db.Integer(10),
                                 nullable=False,
                                 server_default='0')
    status = db.Column(db.Char(2),
                       nullable=False,
                       index=True,
                       server_default='ok')
    round_name = db.Column(db.String(255), nullable=False, server_default='')
    restriction = db.Column(db.String(50), nullable=False, server_default='')
    in_reply_to_id_cmtRECORDCOMMENT = db.Column(db.Integer(15, unsigned=True),
                                                db.ForeignKey(id),
                                                nullable=False,
                                                server_default='0')
    reply_order_cached_data = db.Column(db.Binary, nullable=True)
    bibrec = db.relationship(Bibrec, backref='recordcomments')
    user = db.relationship(User, backref='recordcomments')
    replies = db.relationship('CmtRECORDCOMMENT',
                              backref=db.backref('parent',
                                                 remote_side=[id],
                                                 order_by=date_creation))

    @property
    def is_deleted(self):
        """Check if is deleted."""
        return self.status != 'ok'

    def is_collapsed(self, id_user):
        """Return true if the comment is collapsed by user."""
        return CmtCOLLAPSED.query.filter(
            db.and_(CmtCOLLAPSED.id_bibrec == self.id_bibrec,
                    CmtCOLLAPSED.id_cmtRECORDCOMMENT == self.id,
                    CmtCOLLAPSED.id_user == id_user)).count() > 0

    @session_manager
    def collapse(self, id_user):
        """Collapse comment beloging to user."""
        c = CmtCOLLAPSED(id_bibrec=self.id_bibrec,
                         id_cmtRECORDCOMMENT=self.id,
                         id_user=id_user)
        db.session.add(c)
        db.session.commit()

    def expand(self, id_user):
        """Expand comment beloging to user."""
        CmtCOLLAPSED.query.filter(
            db.and_(CmtCOLLAPSED.id_bibrec == self.id_bibrec,
                    CmtCOLLAPSED.id_cmtRECORDCOMMENT == self.id,
                    CmtCOLLAPSED.id_user == id_user)).delete(
                        synchronize_session=False)

    __table_args__ = (db.Index('cmtRECORDCOMMENT_reply_order_cached_data',
                               reply_order_cached_data,
                               mysql_length=40), db.Model.__table_args__)

    @classmethod
    def count(cls, *criteria, **filters):
        """Count how many comments."""
        return cls.query.filter(*criteria).filter_by(**filters).count()