class FileInstance(db.Model, Timestamp): """Model for storing files. A file instance represents a file on disk. A file instance may be linked from many objects, while an object can have one and only one file instance. A file instance also records the storage class, size and checksum of the file on disk. Additionally, a file instance can be read only in case the storage layer is not capable of writing to the file (e.g. can typically be used to link to files on externally controlled storage). """ __tablename__ = 'files_files' id = db.Column( UUIDType, primary_key=True, default=uuid.uuid4, ) """Identifier of file.""" uri = db.Column(db.Text().with_variant(mysql.VARCHAR(255), 'mysql'), unique=True, nullable=True) """Location of file.""" storage_class = db.Column(db.String(1), nullable=True) """Storage class of file.""" size = db.Column(db.BigInteger, default=0, nullable=True) """Size of file.""" checksum = db.Column(db.String(255), nullable=True) """String representing the checksum of the object.""" readable = db.Column(db.Boolean(name='readable'), default=True, nullable=False) """Defines if the file is read only.""" writable = db.Column(db.Boolean(name='writable'), default=True, nullable=False) """Defines if file is writable. This property is used to create a file instance prior to having the actual file at the given URI. This is useful when e.g. copying a file instance. """ last_check_at = db.Column(db.DateTime, nullable=True) """Timestamp of last fixity check.""" last_check = db.Column(db.Boolean(name='last_check'), default=True) """Result of last fixity check.""" @validates('uri') def validate_uri(self, key, uri): """Validate uri.""" if len(uri) > current_app.config['FILES_REST_FILE_URI_MAX_LEN']: raise ValueError('FileInstance URI too long ({0}).'.format( len(uri))) return uri @classmethod def get(cls, file_id): """Get a file instance.""" return cls.query.filter_by(id=file_id).one_or_none() @classmethod def get_by_uri(cls, uri): """Get a file instance by URI.""" assert uri is not None return cls.query.filter_by(uri=uri).one_or_none() @classmethod def create(cls): """Create a file instance. Note, object is only added to the database session. """ obj = cls( id=uuid.uuid4(), writable=True, readable=False, size=0, ) db.session.add(obj) return obj def delete(self): """Delete a file instance. The file instance can be deleted if it has no references from other objects. The caller is responsible to test if the file instance is writable and that the disk file can actually be removed. .. note:: Normally you should use the Celery task to delete a file instance, as this method will not remove the file on disk. """ self.query.filter_by(id=self.id).delete() return self def storage(self, **kwargs): """Get storage interface for object. Uses the applications storage factory to create a storage interface that can be used for this particular file instance. :returns: Storage interface. """ return current_files_rest.storage_factory(fileinstance=self, **kwargs) @ensure_readable() def update_checksum(self, progress_callback=None, chunk_size=None, checksum_kwargs=None, **kwargs): """Update checksum based on file.""" self.checksum = self.storage(**kwargs).checksum( progress_callback=progress_callback, chunk_size=chunk_size, **(checksum_kwargs or {})) def clear_last_check(self): """Clear the checksum of the file.""" with db.session.begin_nested(): self.last_check = None self.last_check_at = datetime.utcnow() return self def verify_checksum(self, progress_callback=None, chunk_size=None, throws=True, checksum_kwargs=None, **kwargs): """Verify checksum of file instance. :param bool throws: If `True`, exceptions raised during checksum calculation will be re-raised after logging. If set to `False`, and an exception occurs, the `last_check` field is set to `None` (`last_check_at` of course is updated), since no check actually was performed. :param dict checksum_kwargs: Passed as `**kwargs`` to ``storage().checksum``. """ try: real_checksum = self.storage(**kwargs).checksum( progress_callback=progress_callback, chunk_size=chunk_size, **(checksum_kwargs or {})) except Exception as exc: current_app.logger.exception(str(exc)) if throws: raise real_checksum = None with db.session.begin_nested(): self.last_check = (None if real_checksum is None else (self.checksum == real_checksum)) self.last_check_at = datetime.utcnow() return self.last_check @ensure_writable() def init_contents(self, size=0, **kwargs): """Initialize file.""" self.set_uri(*self.storage(**kwargs).initialize(size=size), readable=False, writable=True) @ensure_writable() def update_contents(self, stream, seek=0, size=None, chunk_size=None, progress_callback=None, **kwargs): """Save contents of stream to this file. :param obj: ObjectVersion instance from where this file is accessed from. :param stream: File-like stream. """ self.checksum = None return self.storage(**kwargs).update( stream, seek=seek, size=size, chunk_size=chunk_size, progress_callback=progress_callback) @ensure_writable() def set_contents(self, stream, chunk_size=None, size=None, size_limit=None, progress_callback=None, **kwargs): """Save contents of stream to this file. :param obj: ObjectVersion instance from where this file is accessed from. :param stream: File-like stream. """ self.set_uri(*self.storage( **kwargs).save(stream, chunk_size=chunk_size, size=size, size_limit=size_limit, progress_callback=progress_callback)) @ensure_writable() def copy_contents(self, fileinstance, progress_callback=None, chunk_size=None, **kwargs): """Copy this file instance into another file instance.""" if not fileinstance.readable: raise ValueError('Source file instance is not readable.') if not self.size == 0: raise ValueError('File instance has data.') self.set_uri(*self.storage( **kwargs).copy(fileinstance.storage(**kwargs), chunk_size=chunk_size, progress_callback=progress_callback)) @ensure_readable() def send_file(self, filename, restricted=True, mimetype=None, trusted=False, chunk_size=None, as_attachment=False, **kwargs): """Send file to client.""" return self.storage(**kwargs).send_file( filename, mimetype=mimetype, restricted=restricted, checksum=self.checksum, trusted=trusted, chunk_size=chunk_size, as_attachment=as_attachment, ) def set_uri(self, uri, size, checksum, readable=True, writable=False, storage_class=None): """Set a location of a file.""" self.uri = uri self.size = size self.checksum = checksum self.writable = writable self.readable = readable self.storage_class = \ current_app.config['FILES_REST_DEFAULT_STORAGE_CLASS'] \ if storage_class is None else \ storage_class return self
class IndexStyle(db.Model, Timestamp): """Index style.""" __tablename__ = 'index_style' id = db.Column(db.String(100), primary_key=True) """identifier for index style setting.""" width = db.Column(db.Text, nullable=False, default='') """Index area width.""" height = db.Column(db.Text, nullable=False, default='') """Index area height.""" index_link_enabled = db.Column(db.Boolean(name='index_link_enabled'), nullable=False, default=False) @classmethod def create(cls, community_id, **data): """Create.""" try: with db.session.begin_nested(): obj = cls(id=community_id, **data) db.session.add(obj) db.session.commit() return obj except Exception as ex: current_app.logger.debug(ex) db.session.rollback() return @classmethod def get(cls, community_id): """Get a style.""" return cls.query.filter_by(id=community_id).one_or_none() @classmethod def update(cls, community_id, **data): """ Update the index detail info. :param index_id: Identifier of the index. :param detail: new index info for update. :return: Updated index info """ try: with db.session.begin_nested(): style = cls.get(community_id) if not style: return for k, v in data.items(): if "width" in k or "height" in k: setattr(style, k, v) db.session.merge(style) db.session.commit() return style except Exception as ex: current_app.logger.debug(ex) db.session.rollback() return
class Schema(db.Model): """Model defining analysis JSON schemas.""" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128), unique=False, nullable=False) fullname = db.Column(db.String(128), unique=False, nullable=True) # version major = db.Column(db.Integer, unique=False, nullable=False, default=1) minor = db.Column(db.Integer, unique=False, nullable=False, default=0) patch = db.Column(db.Integer, unique=False, nullable=False, default=0) experiment = db.Column(db.String(128), unique=False, nullable=True) config = db.Column(json_type, default=lambda: dict(), nullable=False) deposit_schema = db.Column(json_type, default=lambda: dict(), nullable=True) deposit_options = db.Column(json_type, default=lambda: dict(), nullable=True) deposit_mapping = db.Column(json_type, default=lambda: dict(), nullable=True) record_schema = db.Column(json_type, default=lambda: dict(), nullable=True) record_options = db.Column(json_type, default=lambda: dict(), nullable=True) record_mapping = db.Column(json_type, default=lambda: dict(), nullable=True) use_deposit_as_record = db.Column(db.Boolean(create_constraint=False), unique=False, default=False) is_indexed = db.Column(db.Boolean(create_constraint=False), unique=False, default=False) created = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False) updated = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False) __tablename__ = 'schema' __table_args__ = (UniqueConstraint('name', 'major', 'minor', 'patch', name='unique_schema_version'), ) def __init__(self, *args, **kwargs): """Possible to set version from string.""" version = kwargs.pop('version', None) if version: self.version = version super(Schema, self).__init__(*args, **kwargs) def __getattribute__(self, attr): """Map record attribute to deposit one, if use_deposit_as_record is ```True```.""" if attr in SERVE_DEPOSIT_AS_RECORD_MAP.keys( ) and self.use_deposit_as_record: attr = SERVE_DEPOSIT_AS_RECORD_MAP.get(attr) return object.__getattribute__(self, attr) def __str__(self): """Stringify schema object.""" return '{name}-v{version}'.format(name=self.name, version=self.version) @property def version(self): """Return stringified version.""" return "{0}.{1}.{2}".format(self.major, self.minor, self.patch) @version.setter def version(self, string): """Set version.""" matched = re.match(r"(\d+)\.(\d+)\.(\d+)", string) if matched is None: raise ValueError( 'Version has to be passed as string <major>.<minor>.<patch>') self.major, self.minor, self.patch = matched.groups() @property def deposit_path(self): """Return deposit schema path.""" prefix = current_app.config['SCHEMAS_DEPOSIT_PREFIX'] path = urljoin(prefix, "{0}.json".format(self)) return path @property def record_path(self): """Return record schema path.""" prefix = current_app.config['SCHEMAS_RECORD_PREFIX'] path = urljoin(prefix, "{0}.json".format(self)) return path @property def deposit_index(self): """Get deposit index name.""" path = urljoin(current_app.config['SCHEMAS_DEPOSIT_PREFIX'], str(self)) return name_to_es_name(path) @property def record_index(self): """Get record index name.""" path = urljoin(current_app.config['SCHEMAS_RECORD_PREFIX'], str(self)) return name_to_es_name(path) @property def deposit_aliases(self): """Get ES deposits aliases.""" name = name_to_es_name(self.name) return ['deposits', 'deposits-records', 'deposits-{}'.format(name)] @property def record_aliases(self): """Get ES records aliases.""" name = name_to_es_name(self.name) return ['records', 'records-{}'.format(name)] @validates('name') def validate_name(self, key, name): """Validate if name is ES compatible.""" if any(x in ES_FORBIDDEN for x in name): raise ValueError('Name cannot contain the following characters' '[, ", *, \\, <, | , , , > , ?]') return name def serialize(self, resolve=False): """Serialize schema model.""" serializer = resolved_schemas_serializer if resolve else schema_serializer # noqa return serializer.dump(self).data def update(self, **kwargs): """Update schema instance.""" for key, value in kwargs.items(): setattr(self, key, value) return self def add_read_access_for_all_users(self): """Give read access to all authenticated users.""" assert self.id db.session.add( ActionSystemRoles.allow(SchemaReadAction(self.id), role=authenticated_user)) db.session.flush() def give_admin_access_for_user(self, user): """Give admin access for users.""" assert self.id db.session.add(ActionUsers.allow(SchemaAdminAction(self.id), user=user)) db.session.flush() @classmethod def get_latest(cls, name): """Get the latest version of schema with given name.""" latest = cls.query \ .filter_by(name=name) \ .order_by(cls.major.desc(), cls.minor.desc(), cls.patch.desc()) \ .first() if latest: return latest else: raise JSONSchemaNotFound(schema=name) @classmethod def get(cls, name, version): """Get schema by name and version.""" matched = re.match(r"(\d+).(\d+).(\d+)", version) if matched is None: raise ValueError( 'Version has to be passed as string <major>.<minor>.<patch>') major, minor, patch = matched.groups() try: schema = cls.query \ .filter_by(name=name, major=major, minor=minor, patch=patch) \ .one() except NoResultFound: raise JSONSchemaNotFound("{}-v{}".format(name, version)) return schema
class User(db.Model, Timestamp, UserMixin): """User data model.""" __tablename__ = "accounts_user" id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) """User email.""" password = db.Column(db.String(255)) """User password.""" active = db.Column(db.Boolean(name='active')) """Flag to say if the user is active or not .""" confirmed_at = db.Column(db.DateTime) """When the user confirmed the email address.""" roles = db.relationship('Role', secondary=userrole, backref=db.backref('users', lazy='dynamic')) """List of the user's roles.""" # Enables SQLAlchemy version counter version_id = db.Column(db.Integer, nullable=False) """Used by SQLAlchemy for optimistic concurrency control.""" __mapper_args__ = {"version_id_col": version_id} login_info = db.relationship("LoginInformation", back_populates="user", uselist=False, lazy="joined") def _get_login_info_attr(self, attr_name): if self.login_info is None: return None return getattr(self.login_info, attr_name) def _set_login_info_attr(self, attr_name, value): if self.login_info is None: self.login_info = LoginInformation() setattr(self.login_info, attr_name, value) @property def current_login_at(self): """When user logged into the current session.""" return self._get_login_info_attr("current_login_at") @property def current_login_ip(self): """Current user IP address.""" return self._get_login_info_attr("current_login_ip") @property def last_login_at(self): """When the user logged-in for the last time.""" return self._get_login_info_attr("last_login_at") @property def last_login_ip(self): """Last user IP address.""" return self._get_login_info_attr("last_login_ip") @property def login_count(self): """Count how many times the user logged in.""" return self._get_login_info_attr("login_count") @current_login_at.setter def current_login_at(self, value): return self._set_login_info_attr("current_login_at", value) @current_login_ip.setter def current_login_ip(self, value): return self._set_login_info_attr("current_login_ip", value) @last_login_at.setter def last_login_at(self, value): return self._set_login_info_attr("last_login_at", value) @last_login_ip.setter def last_login_ip(self, value): return self._set_login_info_attr("last_login_ip", value) @login_count.setter def login_count(self, value): return self._set_login_info_attr("login_count", value) def __str__(self): """Representation.""" return 'User <id={0.id}, email={0.email}>'.format(self)
class ItemType(db.Model, Timestamp): """Represent an item type. The ItemType object contains a ``created`` and a ``updated`` properties that are automatically updated. """ # Enables SQLAlchemy-Continuum versioning __versioned__ = {} __tablename__ = 'item_type' id = db.Column(db.Integer(), primary_key=True, autoincrement=True) """Identifier of item type.""" name_id = db.Column(db.Integer(), db.ForeignKey('item_type_name.id', name='fk_item_type_name_id'), nullable=False) """Name identifier of item type.""" item_type_name = db.relationship('ItemTypeName', backref=db.backref( 'item_type', lazy='dynamic', order_by=desc('item_type.tag'))) """Name information from ItemTypeName class.""" harvesting_type = db.Column(db.Boolean(name='harvesting_type'), nullable=False, default=False) schema = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store schema in JSON format. When you create a new ``item type`` the ``schema`` field value should never be ``NULL``. Default value is an empty dict. ``NULL`` value means that the record metadata has been deleted. """ form = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store schema form in JSON format. When you create a new ``item type`` the ``form`` field value should never be ``NULL``. Default value is an empty dict. ``NULL`` value means that the record metadata has been deleted. """ render = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store page render information in JSON format. When you create a new ``item type`` the ``render`` field value should never be ``NULL``. Default value is an empty dict. ``NULL`` value means that the record metadata has been deleted. """ tag = db.Column(db.Integer, nullable=False) """Tag of item type.""" version_id = db.Column(db.Integer, nullable=False) """Used by SQLAlchemy for optimistic concurrency control.""" edit_notes = db.relationship('ItemTypeEditHistory', order_by='ItemTypeEditHistory.created', backref='item_type') """Used to reference whole edit history.""" is_deleted = db.Column(db.Boolean(name='deleted'), nullable=False, default=False) """Status of item type.""" __mapper_args__ = {'version_id_col': version_id} @property def latest_edit_history(self): """Get latest edit note of self.""" return self.edit_notes[-1].notes if self.edit_notes else {}
class SessionLifetime(db.Model): """Session Lifetime model. Stores session life_time create_date for Session. """ __tablename__ = 'session_lifetime' id = db.Column(db.Integer, primary_key=True, autoincrement=True) _lifetime = db.Column('lifetime', db.Integer, nullable=False, default=30) """ Session Life Time default units: minutes """ create_date = db.Column(db.DateTime, default=datetime.now) is_delete = db.Column(db.Boolean(name='delete'), default=False, nullable=False) @hybrid_property def lifetime(self): """ Get lifetime. :return: Lifetime. """ return self._lifetime @lifetime.setter def lifetime(self, lifetime): """ Set lifetime. :param lifetime: :return: Lifetime. """ self._lifetime = lifetime def create(self, lifetime=None): """ Save session lifetime. :param lifetime: default None :return: """ try: with db.session.begin_nested(): if lifetime: self.lifetime = lifetime self.is_delete = False db.session.add(self) db.session.commit() except BaseException: db.session.rollback() raise return self @classmethod def get_validtime(cls): """Get valid lifetime. :returns: A :class:`~weko_admin.models.SessionLifetime` instance or ``None``. """ return cls.query.filter_by(is_delete=False).one_or_none() @property def is_anonymous(self): """Return whether this UserProfile is anonymous.""" return False
class BillingPermission(db.Model): """Database for Billing Permission.""" __tablename__ = 'billing_permission' user_id = db.Column(db.Integer(), primary_key=True, nullable=False, unique=True) is_active = db.Column(db.Boolean(name='active'), default=True, nullable=False) @classmethod def create(cls, user_id, is_active=True): """Create new user can access billing file. :param user_id: user's id :param is_active: access state :return: Unit if create succesfully """ try: obj = BillingPermission() with db.session.begin_nested(): obj.user_id = user_id obj.is_active = is_active db.session.add(obj) db.session.commit() except BaseException as ex: db.session.rollback() current_app.logger.debug(ex) raise return cls @classmethod def activation(cls, user_id, is_active): """Change access state of user. :param user_id: user's id :param is_active: access state :return: Updated records """ try: with db.session.begin_nested(): billing_data = cls.query.filter_by( user_id=user_id).one_or_none() if billing_data: billing_data.is_active = is_active db.session.merge(billing_data) else: cls.create(user_id, is_active) current_app.logger.info('New user is created!') db.session.commit() except BaseException as ex: db.session.rollback() current_app.logger.debug(ex) raise return cls @classmethod def get_billing_information_by_id(cls, user_id): """Get billing information by user id. :param user_id: user's id :return: Record or none """ try: billing_information = cls.query.filter_by( user_id=user_id).one_or_none() except Exception as ex: current_app.logger.debug(ex) raise return billing_information
class Group(db.Model): """Group data model.""" __tablename__ = 'accounts_group' PRIVACY_POLICIES = [ (PrivacyPolicy.PUBLIC, _('Public')), (PrivacyPolicy.MEMBERS, _('Group members')), (PrivacyPolicy.ADMINS, _('Group admins')), ] """Privacy policy choices.""" SUBSCRIPTION_POLICIES = [ (SubscriptionPolicy.OPEN, _('Open')), (SubscriptionPolicy.APPROVAL, _('Open with approval')), (SubscriptionPolicy.CLOSED, _('Closed')), ] """Subscription policy choices.""" id = db.Column(db.Integer, nullable=False, primary_key=True, autoincrement=True) """Group identifier.""" name = db.Column(db.String(255), nullable=False, unique=True, index=True, info=dict( label=_('Name'), description=_('Required. A name of a group.'), )) """Name of group.""" description = db.Column( db.Text, nullable=True, default='', info=dict( label=_('Description'), description=_('Optional. A short description of the group.' ' Default: Empty'), )) """Description of group.""" is_managed = db.Column(db.Boolean(name='managed'), default=False, nullable=False) """Determine if group is system managed.""" privacy_policy = db.Column(ChoiceType(PRIVACY_POLICIES, impl=db.String(1)), nullable=False, default=PrivacyPolicy.MEMBERS, info=dict( label=_('Privacy Policy'), widget=RadioGroupWidget( PrivacyPolicy.descriptions), )) """Policy for who can view the list of group members.""" subscription_policy = db.Column(ChoiceType(SUBSCRIPTION_POLICIES, impl=db.String(1)), nullable=False, default=SubscriptionPolicy.APPROVAL, info=dict( label=_('Subscription Policy'), widget=RadioGroupWidget( SubscriptionPolicy.descriptions), )) """Policy for how users can be subscribed to the group.""" created = db.Column(db.DateTime, nullable=False, default=datetime.now) """Creation timestamp.""" modified = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now) """Modification timestamp.""" def get_id(self): """Get group id. :returns: the group id """ return self.id @classmethod def create(cls, name=None, description='', privacy_policy=None, subscription_policy=None, is_managed=False, admins=None): """Create a new group. :param is_managed: :param name: Name of group. Required and must be unique. :param description: Description of group. Default: ``''`` :param privacy_policy: PrivacyPolicy :param subscription_policy: SubscriptionPolicy :param admins: list of user and/or group objects. Default: ``[]`` :returns: Newly created group :raises: IntegrityError: if group with given name already exists """ assert name assert privacy_policy is None or PrivacyPolicy.validate(privacy_policy) assert subscription_policy is None or SubscriptionPolicy \ .validate(subscription_policy) assert admins is None or isinstance(admins, list) with db.session.begin_nested(): obj = cls( name=name, description=description, privacy_policy=privacy_policy, subscription_policy=subscription_policy, is_managed=is_managed, ) db.session.add(obj) for a in admins or []: db.session.add( GroupAdmin(group=obj, admin_id=a.get_id(), admin_type=resolve_admin_type(a))) db.session.commit() return obj def delete(self): """Delete a group and all associated memberships.""" with db.session.begin_nested(): Membership.query_by_group(self).delete() GroupAdmin.query_by_group(self).delete() GroupAdmin.query_by_admin(self).delete() db.session.delete(self) db.session.commit() def update(self, name=None, description=None, privacy_policy=None, subscription_policy=None, is_managed=None): """Update group. :param is_managed: :param name: Name of group. :param description: Description of group. :param privacy_policy: PrivacyPolicy :param subscription_policy: SubscriptionPolicy :returns: Updated group """ with db.session.begin_nested(): if name is not None: self.name = name if description is not None: self.description = description if (privacy_policy is not None and PrivacyPolicy.validate(privacy_policy)): self.privacy_policy = privacy_policy if (subscription_policy is not None and SubscriptionPolicy.validate(subscription_policy)): self.subscription_policy = subscription_policy if is_managed is not None: self.is_managed = is_managed db.session.merge(self) db.session.commit() return self @classmethod def get_group_list(cls): """ Get Group List. :return: Group list object. """ grouplist = Group.query.all() obj = {} for k in grouplist: obj[str(k.id)] = k.name return obj @classmethod def get_by_name(cls, name): """Query group by a group name. :param name: Name of a group to search for. :returns: Group object or None. """ try: return cls.query.filter_by(name=name).one() except NoResultFound: return None @classmethod def query_by_names(cls, names): """Query group by a list of group names. :param list names: List of the group names. :returns: Query object. """ assert isinstance(names, list) return cls.query.filter(cls.name.in_(names)) @classmethod def query_by_user(cls, user, with_pending=False, eager=False): """Query group by user. :param user: User object. :param bool with_pending: Whether to include pending users. :param bool eager: Eagerly fetch group members. :returns: Query object. """ q1 = Group.query.join(Membership).filter_by(user_id=user.get_id()) if not with_pending: q1 = q1.filter_by(state=MembershipState.ACTIVE) if eager: q1 = q1.options(joinedload(Group.members)) q2 = Group.query.join(GroupAdmin).filter_by( admin_id=user.get_id(), admin_type=resolve_admin_type(user)) if eager: q2 = q2.options(joinedload(Group.members)) query = q1.union(q2).with_entities(Group.id) return Group.query.filter(Group.id.in_(query)) @classmethod def search(cls, query, q): """Modify query as so include only specific group names. :param query: Query object. :param str q: Search string. :returns: Query object. """ return query.filter(Group.name.like('%{0}%'.format(q))) def add_admin(self, admin): """Invite an admin to a group. :param admin: Object to be added as an admin. :returns: GroupAdmin object. """ return GroupAdmin.create(self, admin) def remove_admin(self, admin): """Remove an admin from group (independent of membership state). :param admin: Admin to be removed from group. """ return GroupAdmin.delete(self, admin) def add_member(self, user, state=MembershipState.ACTIVE): """Invite a user to a group. :param user: User to be added as a group member. :param state: MembershipState. Default: MembershipState.ACTIVE. :returns: Membership object or None. """ return Membership.create(self, user, state) def remove_member(self, user): """Remove a user from a group (independent of their membership state). :param user: User to be removed from group members. """ return Membership.delete(self, user) def invite(self, user, admin=None): """Invite a user to a group (should be done by admins). Wrapper around ``add_member()`` to ensure proper membership state. :param user: User to invite. :param admin: Admin doing the action. If provided, user is only invited if the object is an admin for this group. Default: None. :returns: Newly created Membership or None. """ if admin is None or self.is_admin(admin): return self.add_member(user, state=MembershipState.PENDING_USER) return None def invite_by_emails(self, emails): """Invite users to a group by emails. :param list emails: Emails of users that shall be invited. :returns list: Newly created Memberships or Nones. """ # assert emails is not None and isinstance(emails, list) results = [] for email in emails: try: user = User.query.filter_by(email=email).one() results.append(self.invite(user)) except NoResultFound: results.append(None) return results def subscribe(self, user): """Subscribe a user to a group (done by users). Wrapper around ``add_member()`` which checks subscription policy. :param user: User to subscribe. :returns: Newly created Membership or None. """ if self.subscription_policy == SubscriptionPolicy.OPEN: return self.add_member(user) elif self.subscription_policy == SubscriptionPolicy.APPROVAL: return self.add_member(user, state=MembershipState.PENDING_ADMIN) elif self.subscription_policy == SubscriptionPolicy.CLOSED: return None def is_admin(self, admin): """Verify if given admin is the group admin. :param admin: Admin object. :returns: True or False. """ if GroupAdmin.get(self, admin) is not None: return True return False def is_member(self, user, with_pending=False): """Verify if given user is a group member. :param user: User object. :param bool with_pending: Whether to include pending users or not. :returns: True or False. """ m = Membership.get(self, user) if m is not None: if with_pending: return True elif m.state == MembershipState.ACTIVE: return True return False def can_see_members(self, user): """Determine if given user can see other group members. :param user: User object. :returns: True or False. """ if self.privacy_policy == PrivacyPolicy.PUBLIC: return True elif self.privacy_policy == PrivacyPolicy.MEMBERS: return self.is_member(user) or self.is_admin(user) elif self.privacy_policy == PrivacyPolicy.ADMINS: return self.is_admin(user) def can_edit(self, user): """Determine if user can edit group data. :param user: User object. :returns: True or False. """ if self.is_managed: return False else: return self.is_admin(user) def can_invite_others(self, user): """Determine if user can invite people to a group. Be aware that this check is independent from the people (users) which are going to be invited. The checked user is the one who invites someone, NOT who is going to be invited. :param user: User object. :returns: True or False. """ if self.is_managed: return False elif self.is_admin(user): return True elif self.subscription_policy != SubscriptionPolicy.CLOSED: return True else: return False def can_leave(self, user): """Determine if user can leave a group. :param user: User object. :returns: True or False. """ if self.is_managed: return False else: return self.is_member(user) def members_count(self): """Determine members count. :returns: Number of memberships. """ return Membership.query_by_group(self.get_id()).count()
class Client(db.Model): """A client is the app which want to use the resource of a user. It is suggested that the client is registered by a user on your site, but it is not required. The client should contain at least these information: client_id: A random string client_secret: A random string client_type: A string represents if it is confidential redirect_uris: A list of redirect uris default_redirect_uri: One of the redirect uris default_scopes: Default scopes of the client But it could be better, if you implemented: allowed_grant_types: A list of grant types allowed_response_types: A list of response types validate_scopes: A function to validate scopes """ __tablename__ = 'oauth2server_client' name = db.Column( db.String(40), info=dict(label=_('Name'), description=_('Name of application (displayed to users).'), validators=[validators.DataRequired()])) """Human readable name of the application.""" description = db.Column(db.Text(), default=u'', info=dict( label=_('Description'), description=_( 'Optional. Description of the application' ' (displayed to users).'), )) """Human readable description.""" website = db.Column( URLType(), info=dict( label=_('Website URL'), description=_('URL of your application (displayed to users).'), ), default=u'', ) user_id = db.Column(db.ForeignKey( User.id, ondelete='CASCADE', ), nullable=True, index=True) """Creator of the client application.""" client_id = db.Column(db.String(255), primary_key=True) """Client application ID.""" client_secret = db.Column(db.String(255), unique=True, index=True, nullable=False) """Client application secret.""" is_confidential = db.Column(db.Boolean(name='is_confidential'), default=True) """Determine if client application is public or not.""" is_internal = db.Column(db.Boolean(name='is_internal'), default=False) """Determins if client application is an internal application.""" _redirect_uris = db.Column(db.Text) """A newline-separated list of redirect URIs. First is the default URI.""" _default_scopes = db.Column(db.Text) """A space-separated list of default scopes of the client. The value of the scope parameter is expressed as a list of space-delimited, case-sensitive strings. """ user = db.relationship(User, backref=db.backref( "oauth2clients", cascade="all, delete-orphan", )) """Relationship to user.""" @property def allowed_grant_types(self): """Return allowed grant types.""" return current_app.config['OAUTH2SERVER_ALLOWED_GRANT_TYPES'] @property def allowed_response_types(self): """Return allowed response types.""" return current_app.config['OAUTH2SERVER_ALLOWED_RESPONSE_TYPES'] # def validate_scopes(self, scopes): # return self._validate_scopes @property def client_type(self): """Return client type.""" if self.is_confidential: return 'confidential' return 'public' @property def redirect_uris(self): """Return redirect uris.""" if self._redirect_uris: return self._redirect_uris.splitlines() return [] @redirect_uris.setter def redirect_uris(self, value): """Validate and store redirect URIs for client.""" if isinstance(value, six.text_type): value = value.split("\n") value = [v.strip() for v in value] for v in value: validate_redirect_uri(v) self._redirect_uris = "\n".join(value) or "" @property def default_redirect_uri(self): """Return default redirect uri.""" try: return self.redirect_uris[0] except IndexError: pass @property def default_scopes(self): """List of default scopes for client.""" if self._default_scopes: return self._default_scopes.split(" ") return [] @default_scopes.setter def default_scopes(self, scopes): """Set default scopes for client.""" validate_scopes(scopes) self._default_scopes = " ".join(set(scopes)) if scopes else "" def validate_scopes(self, scopes): """Validate if client is allowed to access scopes.""" try: validate_scopes(scopes) return True except ScopeDoesNotExists: return False def gen_salt(self): """Generate salt.""" self.reset_client_id() self.reset_client_secret() def reset_client_id(self): """Reset client id.""" self.client_id = gen_salt( current_app.config.get('OAUTH2SERVER_CLIENT_ID_SALT_LEN')) def reset_client_secret(self): """Reset client secret.""" self.client_secret = gen_salt( current_app.config.get('OAUTH2SERVER_CLIENT_SECRET_SALT_LEN')) @property def get_users(self): """Get number of users.""" no_users = Token.query.filter_by(client_id=self.client_id, is_personal=False, is_internal=False).count() return no_users
class OARepoCommunityModel(db.Model, Timestamp): __tablename__ = 'oarepo_communities' __table_args__ = {'extend_existing': True} __versioned__ = {'versioning': False} id = db.Column( db.String(63), primary_key=True, ) """Primary Community identifier slug.""" title = db.Column(db.String(128), ) """Community title name.""" type = db.Column(ChoiceType(choices=OAREPO_COMMUNITIES_TYPES, impl=db.VARCHAR(16)), default='other', nullable=False) """Community type or focus.""" json = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store community metadata in JSON format.""" is_deleted = db.Column( db.Boolean(name="ck_oarepo_community_metadata_is_deleted"), nullable=True, default=False) """Was the OARepo community soft-deleted.""" roles = db.relationship('Role', secondary=oarepo_communities_role, backref=db.backref('community', lazy='dynamic')) def delete_roles(self): """Delete roles associated with this community.""" with db.session.begin_nested(): for r in self.roles: db.session.delete(r) def delete(self): """Mark the community for deletion.""" self.is_deleted = True self.delete_roles() def _validate_role_action(self, role, action, system=False): if action not in current_oarepo_communities.allowed_actions: raise AttributeError(f'Action {action} not allowed') if system: if role.value not in current_access.system_roles: raise AttributeError(f'Role {role} not in system roles') elif role not in self.roles: raise AttributeError(f'Role {role} not in community roles') def allow_action(self, role, action, system=False): """Allow action for a role.""" self._validate_role_action(role, action, system) with db.session.begin_nested(): if system: ar = ActionSystemRoles.query.filter_by( action=action, argument=self.id, role_name=role.value).first() if not ar: ar = ActionSystemRoles(action=action, argument=self.id, role_name=role.value) else: ar = ActionRoles.query.filter_by(action=action, argument=self.id, role_id=role.id).first() if not ar: ar = ActionRoles(action=action, argument=self.id, role=role) db.session.add(ar) return ar def deny_action(self, role, action, system=False): """Deny action for a role.""" self._validate_role_action(role, action, system) with db.session.begin_nested(): if system: ar = ActionSystemRoles.query.filter_by( action=action, argument=self.id, role_name=role.value).all() else: ar = ActionRoles.query.filter_by(action=action, argument=self.id, role=role).all() for a in ar: db.session.delete(a) @property def actions(self): ars = ActionRoles.query \ .filter_by(argument=self.id) \ .order_by(ActionRoles.action).all() sars = ActionSystemRoles.query \ .filter_by(argument=self.id) \ .order_by(ActionSystemRoles.action).all() ars = [{k: list(g)} for k, g in groupby(ars, key=attrgetter('action'))] sars = [{ k: list(g) } for k, g in groupby(sars, key=attrgetter('action'))] return ars, sars @property def excluded_facets(self): return self.json.get('excluded_facets', {}) def to_json(self): return dict(id=self.id, title=self.title, type=str(self.type), metadata=self.json)
class User(db.Model, Timestamp, UserMixin): """User data model.""" __tablename__ = "accounts_user" id = db.Column(db.Integer, primary_key=True) _username = db.Column("username", db.String(255), nullable=True, unique=True) """Lower-case version of the username, to assert uniqueness.""" _displayname = db.Column("displayname", db.String(255), nullable=True) """Case-preserving version of the username.""" email = db.Column(db.String(255), unique=True) """User email.""" password = db.Column(db.String(255)) """User password.""" active = db.Column(db.Boolean(name="active")) """Flag to say if the user is active or not .""" confirmed_at = db.Column(db.DateTime) """When the user confirmed the email address.""" roles = db.relationship("Role", secondary=userrole, backref=db.backref("users", lazy="dynamic")) """List of the user's roles.""" # Enables SQLAlchemy version counter version_id = db.Column(db.Integer, nullable=False) """Used by SQLAlchemy for optimistic concurrency control.""" _user_profile = db.Column( "profile", json_field, default=lambda: dict(), nullable=True, ) """The user profile as a JSON field.""" _preferences = db.Column( "preferences", json_field, default=lambda: dict(), nullable=True, ) """The user's preferences stored in a JSON field.""" __mapper_args__ = {"version_id_col": version_id} login_info = db.relationship("LoginInformation", back_populates="user", uselist=False, lazy="joined") def __init__(self, *args, **kwargs): """Constructor.""" user_profile = kwargs.pop("user_profile", {}) preferences = kwargs.pop("preferences", {}) preferences.setdefault("visibility", "restricted") preferences.setdefault("email_visibility", "restricted") super().__init__(*args, **kwargs) self.user_profile = user_profile self.preferences = preferences @hybrid_property def username(self): """Get username.""" return self._displayname @username.setter def username(self, username): """Set username. .. note:: The username will be converted to lowercase. The display name will contain the original version. """ if username is None: # if the username can't be validated, a ValueError will be raised self._displayname = None self._username = None else: validate_username(username) self._displayname = username self._username = username.lower() @hybrid_property def user_profile(self): """Get the user profile.""" # NOTE: accessing this property requires an initialized app for config if self._user_profile is None: return None elif not isinstance(self._user_profile, UserProfileDict): return UserProfileDict(**self._user_profile) return self._user_profile @user_profile.setter def user_profile(self, value): """Set the user profile.""" if value is None: self._user_profile = None else: self._user_profile = UserProfileDict(**value) @hybrid_property def preferences(self): """Get the user preferences.""" # NOTE: accessing this property requires an initialized app for config if self._preferences is None: return None elif not isinstance(self._preferences, UserPreferenceDict): self._preferences = UserPreferenceDict(**self._preferences) return self._preferences @preferences.setter def preferences(self, value): """Set the user preferences.""" if value is None: self._preferences = None else: self._preferences = UserPreferenceDict(**value) def _get_login_info_attr(self, attr_name): if self.login_info is None: return None return getattr(self.login_info, attr_name) def _set_login_info_attr(self, attr_name, value): if self.login_info is None: self.login_info = LoginInformation() setattr(self.login_info, attr_name, value) @property def current_login_at(self): """When user logged into the current session.""" return self._get_login_info_attr("current_login_at") @property def current_login_ip(self): """Current user IP address.""" return self._get_login_info_attr("current_login_ip") @property def last_login_at(self): """When the user logged-in for the last time.""" return self._get_login_info_attr("last_login_at") @property def last_login_ip(self): """Last user IP address.""" return self._get_login_info_attr("last_login_ip") @property def login_count(self): """Count how many times the user logged in.""" return self._get_login_info_attr("login_count") @current_login_at.setter def current_login_at(self, value): return self._set_login_info_attr("current_login_at", value) @current_login_ip.setter def current_login_ip(self, value): return self._set_login_info_attr("current_login_ip", value) @last_login_at.setter def last_login_at(self, value): return self._set_login_info_attr("last_login_at", value) @last_login_ip.setter def last_login_ip(self, value): return self._set_login_info_attr("last_login_ip", value) @login_count.setter def login_count(self, value): return self._set_login_info_attr("login_count", value) def __str__(self): """Representation.""" return "User <id={0.id}, email={0.email}>".format(self)
class WidgetItem(db.Model): """Database for WidgetItem.""" __tablename__ = 'widget_items' widget_id = db.Column(db.Integer, primary_key=True, nullable=False) repository_id = db.Column(db.String(100), nullable=False) widget_type = db.Column(db.String(100), nullable=False) settings = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) is_enabled = db.Column(db.Boolean(name='enable'), default=True) is_deleted = db.Column(db.Boolean(name='deleted'), default=False) # # Query Operation # @classmethod def get_by_id(cls, widget_item_id): """Get a widget item by id.""" widget = cls.query.filter_by(widget_id=widget_item_id).one_or_none() return widget @classmethod def get_id_by_repository_and_type(cls, repository, widget_type): """Get id by repository id and widget type. :param repository: Repository id :param widget_type: Widget type :return:Widget Item """ widget_data = cls.query.filter_by(repository_id=repository, widget_type=widget_type, is_deleted=False).all() if not widget_data: return None list_id = list() for data in widget_data: list_id.append(data.widget_id) return list_id @classmethod def get_sequence(cls, session): """Get widget item next sequence. :param session: Session :return: Next sequence. """ if not session: session = db.session seq = Sequence('widget_items_widget_id_seq') next_id = session.execute(seq) return next_id @classmethod def create(cls, widget_data, session): """Create widget item. :param widget_data: widget data :param session: session :return: """ if not session: return None data = cls(**widget_data) session.add(data) @classmethod def update_by_id(cls, widget_item_id, widget_data, session=None): """Update the widget by id. Arguments: widget_item_id {Integer} -- Id of widget widget_data {Dictionary} -- data Returns: widget -- if success """ if not session: session = db.session widget = cls.get_by_id(widget_item_id) if not widget: return for k, v in widget_data.items(): setattr(widget, k, v) session.merge(widget) return widget @classmethod def update_setting_by_id(cls, widget_id, settings): """Update widget setting by widget id. :param widget_id: :param settings: :return: True if update successful """ try: widget_item = cls.get_by_id(widget_id) with db.session.begin_nested(): widget_item.settings = settings db.session.merge(widget_item) db.session.commit() return True except Exception as ex: db.session.rollback() current_app.logger.debug(ex) return False @classmethod def delete_by_id(cls, widget_id, session): """Delete the widget by id. Arguments: widget_id {Integer} -- The widget id Returns: widget -- If success """ widget = cls.get_by_id(widget_id) if not widget: return setattr(widget, 'is_deleted', True) session.merge(widget) return widget
class WidgetDesignPage(db.Model): """Database for menu pages.""" __tablename__ = 'widget_design_page' id = db.Column(db.Integer, primary_key=True, nullable=False) title = db.Column(db.String(100), nullable=True) repository_id = db.Column(db.String(100), nullable=False) url = db.Column(db.String(100), nullable=False, unique=True) template_name = db.Column( # May be used in the future db.String(100), nullable=True) content = db.Column(db.Text(), nullable=True, default='') settings = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) is_main_layout = db.Column(db.Boolean('is_main_layout'), nullable=True) multi_lang_data = db.relationship( 'WidgetDesignPageMultiLangData', backref='widget_design_page', cascade='all, delete-orphan', collection_class=attribute_mapped_collection('lang_code')) @classmethod def create_or_update(cls, repository_id, title, url, content, page_id=0, settings=None, multi_lang_data={}, is_main_layout=False): """Insert new widget design page. :param repository_id: Identifier of the repository :param title: Page title :param url: Page URL :param content: HTML content :param page_id: Page identifier :param settings: Page widget setting data :param multi_lang_data: Multi language data :param is_main_layout: Main layout flash :return: True if successful, otherwise False """ try: prev = cls.query.filter_by(id=int(page_id)).one_or_none() if prev: repository_id = prev.repository_id page = prev or WidgetDesignPage() if not repository_id or not url: return False with db.session.begin_nested(): page.repository_id = repository_id page.title = title page.url = url page.content = content page.settings = settings page.is_main_layout = is_main_layout for lang in multi_lang_data: page.multi_lang_data[lang] = \ WidgetDesignPageMultiLangData( lang, multi_lang_data[lang]) db.session.merge(page) db.session.commit() return True except Exception as ex: db.session.rollback() current_app.logger.debug(ex) raise ex @classmethod def delete(cls, page_id): """Delete widget design page. :param page_id: Page model's id :return: True if successful or False """ if page_id: try: with db.session.begin_nested(): cls.query.filter_by(id=int(page_id)).delete() db.session.commit() return True except BaseException as ex: db.session.rollback() current_app.logger.debug(ex) raise ex return False @classmethod def update_settings(cls, page_id, settings=None): """Update design page setting. :param page_id: Identifier of the page. :param settings: Page widget setting data. :return: True if successful, otherwise False. """ try: page = cls.query.filter_by(id=int(page_id)).one_or_none() if page: with db.session.begin_nested(): page.settings = settings db.session.merge(page) db.session.commit() return True except Exception as ex: db.session.rollback() current_app.logger.debug(ex) raise ex return False @classmethod def update_settings_by_repository_id(cls, repository_id, settings=None): """Update design page setting by repository id. Note: ALL pages belonging to repository will have the same settings. Could be used to make all pages uniform in design. :param repository_id: Repository id. :param settings: Page widget setting data. :return: True if successful, otherwise False. """ try: pages = cls.query.filter_by(repository_id=int(repository_id)).all() for page in pages: with db.session.begin_nested(): page.settings = settings db.session.merge(page) db.session.commit() return True except Exception as ex: db.session.rollback() current_app.logger.debug(ex) return False @classmethod def get_all(cls): """ Get all pages. :return: List of all pages. """ return db.session.query(cls).all() @classmethod def get_all_valid(cls): """ Get all pages with widget settings. :return: List of all pages. """ return db.session.query(cls).filter(cls.settings is not None).all() @classmethod def get_by_id(cls, id): """ Get widget page by id. Raises error if not found etc. :return: Single page object or exception raised. """ return db.session.query(cls).filter_by(id=int(id)).one() @classmethod def get_by_id_or_none(cls, id): """ Get widget page by id without raising exception. :return: Single page object or none. """ return db.session.query(cls).filter_by(id=int(id)).one_or_none() @classmethod def get_by_url(cls, url): """ Get widget page by url. :return: Single page objects or none. """ return db.session.query(cls).filter_by(url=url).one() @classmethod def get_by_repository_id(cls, repository_id): """ Get widget pages for community/repo. :return: Multiple page objects or empty list. """ return db.session.query(cls).filter_by( repository_id=repository_id).all()
class ObjectVersion(db.Model, Timestamp): """Model for storing versions of objects. A bucket stores one or more objects identified by a key. Each object is versioned where each version is represented by an ``ObjectVersion``. An object version can either be 1) a *normal version* which is linked to a file instance, or 2) a *delete marker*, which is *not* linked to a file instance. An normal object version is linked to a physical file on disk via a file instance. This allows for multiple object versions to point to the same file on disk, to optimize storage efficiency (e.g. useful for snapshotting an entire bucket without duplicating the files). A delete marker object version represents that the object at hand was deleted. The latest version of an object is marked using the ``is_head`` property. If the latest object version is a delete marker the object will not be shown in the bucket. """ __tablename__ = 'files_object' bucket_id = db.Column( UUIDType, db.ForeignKey(Bucket.id, ondelete='RESTRICT'), default=uuid.uuid4, primary_key=True, ) """Bucket identifier.""" key = db.Column( db.Text().with_variant(mysql.VARCHAR(255), 'mysql'), primary_key=True, ) """Key identifying the object.""" version_id = db.Column( UUIDType, primary_key=True, default=uuid.uuid4, ) """Identifier for the specific version of an object.""" file_id = db.Column(UUIDType, db.ForeignKey(FileInstance.id, ondelete='RESTRICT'), nullable=True) """File instance for this object version. A null value in this column defines that the object has been deleted. """ _mimetype = db.Column( db.String(255), index=True, nullable=True, ) """MIME type of the object.""" is_head = db.Column(db.Boolean(name='is_head'), nullable=False, default=True) """Defines if object is the latest version.""" # Relationships definitions bucket = db.relationship(Bucket, backref='objects') """Relationship to buckets.""" file = db.relationship(FileInstance, backref='objects') """Relationship to file instance.""" @validates('key') def validate_key(self, key, key_): """Validate key.""" return validate_key(key_) def __repr__(self): """Return representation of location.""" return '{0}:{2}:{1}'.format(self.bucket_id, self.key, self.version_id) @hybrid_property def mimetype(self): """Get MIME type of object.""" if self._mimetype: m = self._mimetype elif self.key: m = mimetypes.guess_type(self.key)[0] return m or 'application/octet-stream' @mimetype.setter def mimetype(self, value): """Setter for MIME type.""" self._mimetype = value @property def basename(self): """Return filename of the object.""" return basename(self.key) @property def deleted(self): """Determine if object version is a delete marker.""" return self.file_id is None @ensure_no_file() @update_bucket_size def set_contents(self, stream, chunk_size=None, size=None, size_limit=None, progress_callback=None): """Save contents of stream to file instance. If a file instance has already been set, this methods raises an ``FileInstanceAlreadySetError`` exception. :param stream: File-like stream. :param size: Size of stream if known. :param chunk_size: Desired chunk size to read stream in. It is up to the storage interface if it respects this value. """ if size_limit is None: size_limit = self.bucket.size_limit self.file = FileInstance.create() self.file.set_contents( stream, size_limit=size_limit, size=size, chunk_size=chunk_size, progress_callback=progress_callback, default_location=self.bucket.location.uri, default_storage_class=self.bucket.default_storage_class, ) return self @ensure_no_file() @update_bucket_size def set_location(self, uri, size, checksum, storage_class=None): """Set only URI location of for object. Useful to link files on externally controlled storage. If a file instance has already been set, this methods raises an ``FileInstanceAlreadySetError`` exception. :param uri: Full URI to object (which can be interpreted by the storage interface). :param size: Size of file. :param checksum: Checksum of file. :param storage_class: Storage class where file is stored () """ self.file = FileInstance() self.file.set_uri(uri, size, checksum, storage_class=storage_class) db.session.add(self.file) return self @ensure_no_file() @update_bucket_size def set_file(self, fileinstance): """Set a file instance.""" self.file = fileinstance return self def send_file(self, restricted=True, trusted=False, **kwargs): """Wrap around FileInstance's send file.""" return self.file.send_file(self.basename, restricted=restricted, mimetype=self.mimetype, trusted=trusted, **kwargs) @ensure_is_previous_version() def restore(self): """Restore this object version to become the latest version. Raises an exception if the object is the latest version. """ # Note, copy calls create which will fail if bucket is locked. return self.copy() @ensure_not_deleted( msg=[ObjectVersionError('Cannot copy a delete marker.')]) def copy(self, bucket=None, key=None): """Copy an object version to a given bucket + object key. The copy operation is handled completely at the metadata level. The actual data on disk is not copied. Instead, the two object versions will point to the same physical file (via the same FileInstance). .. warning:: If the destination object exists, it will be replaced by the new object version which will become the latest version. :param bucket: The bucket (instance or id) to copy the object to. Default: current bucket. :param key: Key name of destination object. Default: current object key. :returns: The copied object version. """ return ObjectVersion.create( self.bucket if bucket is None else as_bucket(bucket), key or self.key, _file_id=self.file_id) @ensure_unlocked(getter=lambda o: not o.bucket.locked) def remove(self): """Permanently remove a specific object version from the database. .. warning:: This by-passes the normal versioning and should only be used when you want to permanently delete a specific object version. Otherwise use :py:data:`ObjectVersion.delete()`. Note the method does not remove the associated file instance which must be garbage collected. :returns: ``self``. """ with db.session.begin_nested(): if self.file_id: self.bucket.size -= self.file.size self.query.filter_by( bucket_id=self.bucket_id, key=self.key, version_id=self.version_id, ).delete() return self @classmethod def create(cls, bucket, key, _file_id=None, stream=None, mimetype=None, version_id=None, **kwargs): """Create a new object in a bucket. The created object is by default created as a delete marker. You must use ``set_contents()`` or ``set_location()`` in order to change this. :param bucket: The bucket (instance or id) to create the object in. :param key: Key of object. :param _file_id: For internal use. :param stream: File-like stream object. Used to set content of object immediately after being created. :param mimetype: MIME type of the file object if it is known. :param kwargs: Keyword arguments passed to ``Object.set_contents()``. """ bucket = as_bucket(bucket) if bucket.locked: raise BucketLockedError() with db.session.begin_nested(): latest_obj = cls.query.filter(cls.bucket == bucket, cls.key == key, cls.is_head.is_(True)).one_or_none() if latest_obj is not None: latest_obj.is_head = False db.session.add(latest_obj) # By default objects are created in a deleted state (i.e. # file_id is null). obj = cls( bucket=bucket, key=key, version_id=version_id or uuid.uuid4(), is_head=True, mimetype=mimetype, ) if _file_id: file_ = _file_id if isinstance(_file_id, FileInstance) else \ FileInstance.get(_file_id) obj.set_file(file_) db.session.add(obj) if stream: obj.set_contents(stream, **kwargs) return obj @classmethod def get(cls, bucket, key, version_id=None): """Fetch a specific object. By default the latest object version is returned, if ``version_id`` is not set. :param bucket: The bucket (instance or id) to get the object from. :param key: Key of object. :param version_id: Specific version of an object. """ filters = [ cls.bucket_id == as_bucket_id(bucket), cls.key == key, ] if version_id: filters.append(cls.version_id == version_id) else: filters.append(cls.is_head.is_(True)) filters.append(cls.file_id.isnot(None)) return cls.query.filter(*filters).one_or_none() @classmethod def get_versions(cls, bucket, key): """Fetch all versions of a specific object. :param bucket: The bucket (instance or id) to get the object from. :param key: Key of object. """ filters = [ cls.bucket_id == as_bucket_id(bucket), cls.key == key, ] return cls.query.filter(*filters).order_by(cls.key, cls.created.desc()) @classmethod def delete(cls, bucket, key): """Delete an object. Technically works by creating a new version which works as a delete marker. :param bucket: The bucket (instance or id) to delete the object from. :param key: Key of object. :param version_id: Specific version to delete. :returns: Created delete marker object if key exists else ``None``. """ bucket_id = as_bucket_id(bucket) obj = cls.get(bucket_id, key) if obj: return cls.create(as_bucket(bucket), key) return None @classmethod def get_by_bucket(cls, bucket, versions=False): """Return query that fetches all the objects in a bucket.""" bucket_id = bucket.id if isinstance(bucket, Bucket) else bucket filters = [ cls.bucket_id == bucket_id, ] if not versions: filters.append(cls.file_id.isnot(None)) filters.append(cls.is_head.is_(True)) return cls.query.filter(*filters).order_by(cls.key, cls.created.desc()) @classmethod def relink_all(cls, old_file, new_file): """Relink all object versions (for a given file) to a new file. .. warning:: Use this method with great care. """ assert old_file.checksum == new_file.checksum assert old_file.id assert new_file.id with db.session.begin_nested(): ObjectVersion.query.filter_by(file_id=str(old_file.id)).update( {ObjectVersion.file_id: str(new_file.id)})
class MembershipRequest(db.Model): """Represent a community member role.""" __tablename__ = 'communities_membership_request' id = db.Column( UUIDType, primary_key=True, default=uuid.uuid4, ) user_id = db.Column( db.Integer, db.ForeignKey(User.id), nullable=True, ) comm_id = db.Column( UUIDType, db.ForeignKey(CommunityMetadata.id), nullable=False, ) email = db.Column( db.String(255), nullable=True, ) role = db.Column(ChoiceType(CommunityRoles, impl=db.CHAR(1)), nullable=True) is_invite = db.Column(db.Boolean(name='is_invite'), nullable=False, default=True) @classmethod def create(cls, comm_id, is_invite, role=None, user_id=None, email=None): """Create Community Membership request.""" try: with db.session.begin_nested(): obj = cls(comm_id=comm_id, user_id=user_id, role=role, is_invite=is_invite, email=email) db.session.add(obj) except IntegrityError: raise CommunityMemberAlreadyExists(comm_id=comm_id, user_id=user_id, role=role) return obj @classmethod def delete(cls, id): """Delete a community membership request.""" try: with db.session.begin_nested(): membership = cls.query.get(id) db.session.delete(membership) except IntegrityError: raise CommunityMemberDoesNotExist(id)
class Token(db.Model): """A bearer token is the final token that can be used by the client.""" __tablename__ = 'oauth2server_token' __table_args__ = ( Index('ix_oauth2server_token_access_token', 'access_token', unique=True, mysql_length=255), Index('ix_oauth2server_token_refresh_token', 'refresh_token', unique=True, mysql_length=255), ) id = db.Column(db.Integer, primary_key=True, autoincrement=True) """Object ID.""" client_id = db.Column(db.String(255), db.ForeignKey(Client.client_id, ondelete='CASCADE'), nullable=False, index=True) """Foreign key to client application.""" client = db.relationship('Client', backref=db.backref('oauth2tokens', cascade="all, delete-orphan")) """SQLAlchemy relationship to client application.""" user_id = db.Column(db.Integer, db.ForeignKey(User.id, ondelete='CASCADE'), nullable=True, index=True) """Foreign key to user.""" user = db.relationship(User, backref=db.backref( "oauth2tokens", cascade="all, delete-orphan", )) """SQLAlchemy relationship to user.""" token_type = db.Column(db.String(255), default='bearer') """Token type - only bearer is supported at the moment.""" access_token = db.Column( EncryptedType( type_in=db.String(255), key=secret_key, ), ) refresh_token = db.Column( EncryptedType( type_in=db.String(255), key=secret_key, engine=NoneAesEngine, ), nullable=True, ) expires = db.Column(db.DateTime, nullable=True) _scopes = db.Column(db.Text) is_personal = db.Column(db.Boolean(name='is_personal'), default=False) """Personal accesss token.""" is_internal = db.Column(db.Boolean(name='is_internal'), default=False) """Determines if token is an internally generated token.""" @property def scopes(self): """Return all scopes. :returns: A list of scopes. """ if self._scopes: return self._scopes.split() return [] @scopes.setter def scopes(self, scopes): """Set scopes. :param scopes: The list of scopes. """ validate_scopes(scopes) self._scopes = " ".join(set(scopes)) if scopes else "" def get_visible_scopes(self): """Get list of non-internal scopes for token. :returns: A list of scopes. """ return [ k for k, s in current_oauth2server.scope_choices() if k in self.scopes ] @classmethod def create_personal(cls, name, user_id, scopes=None, is_internal=False): """Create a personal access token. A token that is bound to a specific user and which doesn't expire, i.e. similar to the concept of an API key. :param name: Client name. :param user_id: User ID. :param scopes: The list of permitted scopes. (Default: ``None``) :param is_internal: If ``True`` it's a internal access token. (Default: ``False``) :returns: A new access token. """ with db.session.begin_nested(): scopes = " ".join(scopes) if scopes else "" c = Client(name=name, user_id=user_id, is_internal=True, is_confidential=False, _default_scopes=scopes) c.gen_salt() t = Token( client_id=c.client_id, user_id=user_id, access_token=gen_salt( current_app.config.get( 'OAUTH2SERVER_TOKEN_PERSONAL_SALT_LEN')), expires=None, _scopes=scopes, is_personal=True, is_internal=is_internal, ) db.session.add(c) db.session.add(t) return t
class AdminLangSettings(db.Model): """System Language Display Setting. Stored target language and registered language """ __tablename__ = 'admin_lang_settings' lang_code = db.Column(db.String(3), primary_key=True, nullable=False, unique=True) lang_name = db.Column(db.String(30), nullable=False) is_registered = db.Column(db.Boolean(name='registered'), default=True) sequence = db.Column(db.Integer, default=0) is_active = db.Column(db.Boolean(name='active'), default=True) @classmethod def parse_result(cls, in_result): """Parse results.""" obj = {} for k in in_result: record = dict() record['lang_code'] = k.lang_code record['lang_name'] = k.lang_name record['is_registered'] = k.is_registered record['sequence'] = k.sequence record['is_active'] = k.is_active obj[k.lang_code] = record json_list = [] for key in obj: json_list.append({ 'lang_code': '{0}'.format(obj[key]['lang_code']), 'lang_name': '{0}'.format(obj[key]['lang_name']), 'is_registered': obj[key]['is_registered'], 'sequence': obj[key]['sequence'] }) sorted_list = sorted(json_list, key=lambda k: k['sequence']) return sorted_list @classmethod def load_lang(cls): """Get language list. :return: A list of language """ lang_list = cls.query.all() return cls.parse_result(lang_list) @classmethod def create(cls, lang_code, lang_name, is_registered, sequence, is_active): """Create language.""" try: dataObj = AdminLangSettings() with db.session.begin_nested(): dataObj.lang_code = lang_code dataObj.lang_name = lang_name dataObj.is_registered = is_registered dataObj.sequence = sequence dataObj.is_active = is_active db.session.add(dataObj) db.session.commit() except BaseException as ex: db.session.rollback() current_app.logger.debug(ex) raise return cls @classmethod def update_lang(cls, lang_code=None, lang_name=None, is_registered=None, sequence=None, is_active=None): """Save list language into database. :param lang_code: input language code :param lang_name: input language name :param is_registered: input boolean is language registered :param sequence: input order number of language :param is_active: input boolean is active of language :return: Updated record """ with db.session.begin_nested(): lang_setting_data = cls.query.filter_by(lang_code=lang_code).one() if lang_code is not None: lang_setting_data.lang_code = lang_code if lang_name is not None: lang_setting_data.lang_name = lang_name if is_registered is not None: lang_setting_data.is_registered = is_registered if sequence is not None: lang_setting_data.sequence = sequence if is_active is not None: lang_setting_data.is_active = is_active db.session.merge(lang_setting_data) db.session.commit() return cls @classmethod def get_lang_code(cls): """Get language code. :return: the language code """ return cls.lang_code @classmethod def get_lang_name(cls): """Get language full name. :return: language full name """ return cls.lang_name @classmethod def get_registered_language(cls): """Get registered languages. :return: All language have registered """ result = cls.query.filter_by(is_registered=True) return cls.parse_result(result) @classmethod def get_active_language(cls): """Get active languages. :return: All languages have activated """ result = cls.query.filter_by(is_active=True).order_by( asc('admin_lang_settings_sequence')) return cls.parse_result(result)
class WidgetMultiLangData(db.Model): """Database for widget multiple language data.""" __tablename__ = 'widget_multi_lang_data' id = db.Column(db.Integer, primary_key=True, nullable=False) widget_id = db.Column(db.Integer, nullable=False) lang_code = db.Column(db.String(3), nullable=False) label = db.Column(db.String(100), nullable=False) description_data = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) is_deleted = db.Column(db.Boolean(name='deleted'), default=False) # # Query Operation # @classmethod def get_by_id(cls, widget_multi_lang_id): """Get widget multi language data by id. Arguments: widget_multilanguage_id {Integer} -- The ID Returns: data -- Widget multi language data """ data = cls.query.filter_by(id=widget_multi_lang_id).one_or_none() return data @classmethod def create(cls, data, session): """Create Widget multi language data. :param data: WWidget multi language data :param session: session :return: """ if not data: return None obj = cls(**data) session.add(obj) return obj @classmethod def get_by_widget_id(cls, widget_id): """Get list widget multilanguage data by widget ID. Arguments: widget_id {Integer} -- The widget id Returns: data -- List widget multilanguage data """ list_data = cls.query.filter_by(widget_id=widget_id).all() return list_data @classmethod def update_by_id(cls, widget_item_id, data): """Update widget multilanguage data by id. Arguments: id {Integer} -- The id data {WidgetMultiLangData} -- The Widget multilanguage data Returns: True -- If deleted """ widget_multi_lang = cls.get_by_id(widget_item_id) if not data: return for k, v in data.items(): setattr(widget_multi_lang, k, v) db.session.merge(widget_multi_lang) return widget_multi_lang @classmethod def delete_by_widget_id(cls, widget_id, session): """Delete widget by id. :param widget_id: id of widget :param session: session of delete :return: """ if not session: session = db.session multi_data = cls.get_by_widget_id(widget_id) if not multi_data: return False for data in multi_data: setattr(data, 'is_deleted', 'True') return True
class LogAnalysisRestrictedCrawlerList(db.Model): """Represent restricted users from txt list to be restricted from logging. The LogAnalysisRestrictedCrawlerList object contains a ``created`` and a ``updated`` properties that are automatically updated. """ __tablename__ = 'loganalysis_restricted_crawler_list' id = db.Column(db.Integer(), primary_key=True, autoincrement=True) list_url = db.Column(db.String(255), nullable=False) is_active = db.Column(db.Boolean(name='activated'), default=True) @classmethod def get_all(cls): """Get all crawler lists. :return: All crawler lists. """ try: all = cls.query.order_by(asc(cls.id)).all() except Exception as ex: current_app.logger.debug(ex) all = [] raise return all @classmethod def get_all_active(cls): """Get all active crawler lists. :return: All active crawler lists. """ try: all = cls.query.filter(cls.is_active.is_(True)) \ .filter(func.length(cls.list_url) > 0).all() except Exception as ex: current_app.logger.debug(ex) all = [] raise return all @classmethod def add_list(cls, crawler_lists): """Add all crawler lists.""" for list_url in crawler_lists: try: with db.session.begin_nested(): record = LogAnalysisRestrictedCrawlerList( list_url=list_url) db.session.add(record) db.session.commit() except Exception as ex: current_app.logger.debug(ex) db.session.rollback() raise return cls @classmethod def update_or_insert_list(cls, crawler_list): """Update or insert LogAnalysisRestrictedCrawlerList list.""" for data in crawler_list: try: new_list = data.get('list_url', '') id = data.get('id', 0) is_active = data.get('is_active', True) with db.session.begin_nested(): current_record = cls.query.filter_by(id=id).one() if current_record: current_record.list_url = new_list current_record.is_active = is_active db.session.merge(current_record) else: db.session.add( LogAnalysisRestrictedCrawlerList( list_url=new_list)) db.session.commit() except BaseException as ex: current_app.logger.debug(ex) db.session.rollback() raise return cls def __iter__(self): """TODO: __iter__.""" for name in dir(LogAnalysisRestrictedCrawlerList): if not name.startswith('__') and not name.startswith('_'): value = getattr(self, name) if isinstance(value, str): yield (name, value)
class WidgetItem(db.Model): """Database for WidgetItem.""" __tablename__ = 'widget_items' widget_id = db.Column(db.Integer, primary_key=True, nullable=False) repository_id = db.Column(db.String(100), nullable=False) widget_type = db.Column(db.String(100), db.ForeignKey(WidgetType.type_id), nullable=False) settings = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) browsing_role = db.Column(db.Text, nullable=True) edit_role = db.Column(db.Text, nullable=True) is_enabled = db.Column(db.Boolean(name='enable'), default=True) is_deleted = db.Column(db.Boolean(name='deleted'), default=False) # # Relation # widgettype = db.relationship(WidgetType, backref=db.backref('repositories', cascade='all, \ delete-orphan')) # # Query Operation # @classmethod def get_by_id(cls, widget_item_id): """Get a widget item by id.""" widget = cls.query.filter_by(widget_id=widget_item_id).one_or_none() return widget @classmethod def get_id_by_repository_and_type(cls, repository, widget_type): """Get id by repository id and widget type. :param repository: Repository id :param widget_type: Widget type :return:Widget Item """ widget_data = cls.query.filter_by(repository_id=repository, widget_type=widget_type, is_deleted=False).all() if not widget_data: return None list_id = list() for data in widget_data: list_id.append(data.widget_id) return list_id @classmethod def get_sequence(cls, session): """Get widget item next sequence. :param session: Session :return: Next sequence. """ if not session: session = db.session seq = Sequence('widget_items_widget_id_seq') next_id = session.execute(seq) return next_id @classmethod def create(cls, widget_data, session): """Create widget item. :param widget_data: widget data :param session: session :return: """ if not session: return None data = cls(**widget_data) session.add(data) @classmethod def update_by_id(cls, widget_item_id, widget_data, session=None): """Update the widget by id. Arguments: widget_item_id {Integer} -- Id of widget widget_data {Dictionary} -- data Returns: widget -- if success """ if not session: session = db.session widget = cls.get_by_id(widget_item_id) if not widget: return for k, v in widget_data.items(): setattr(widget, k, v) session.merge(widget) return widget @classmethod def delete_by_id(cls, widget_id, session): """Delete the widget by id. Arguments: widget_id {Integer} -- The widget id Returns: widget -- If success """ widget = cls.get_by_id(widget_id) if not widget: return setattr(widget, 'is_deleted', 'True') session.merge(widget) return widget
class RankingSettings(db.Model): """Ranking settings.""" __tablename__ = 'ranking_settings' id = db.Column(db.Integer, primary_key=True, autoincrement=True) is_show = db.Column(db.Boolean(name='show'), nullable=False, default=False) new_item_period = db.Column(db.Integer, nullable=False, default=14) statistical_period = db.Column(db.Integer, nullable=False, default=365) display_rank = db.Column(db.Integer, nullable=False, default=10) rankings = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) @classmethod def get(cls, id=0): """Get ranking settings.""" return cls.query.filter_by(id=id).first() @classmethod def update(cls, id=0, data=None): """Update/Create ranking settings.""" try: with db.session.begin_nested(): new_data_flag = False settings = cls.query.filter_by(id=id).first() if not settings: settings = RankingSettings() new_data_flag = True settings.id = id settings.is_show = data.is_show settings.new_item_period = data.new_item_period settings.statistical_period = data.statistical_period settings.display_rank = data.display_rank settings.rankings = data.rankings if new_data_flag: db.session.add(settings) else: db.session.merge(settings) db.session.commit() except BaseException as ex: db.session.rollback() current_app.logger.debug(ex) raise return cls @classmethod def delete(cls, id=0): """Delete settings.""" try: with db.session.begin_nested(): cls.query.filter_by(id=id).delete() db.session.commit() except BaseException as ex: db.session.rollback() current_app.logger.debug(ex) raise ex return cls
class Journal(db.Model, Timestamp): """ Represent an journal. The Journal object contains a ``created``, a ``updated`` properties that are automatically updated. """ __tablename__ = 'journal' id = db.Column(db.BigInteger, primary_key=True, unique=True) """Identifier of the index.""" index_id = db.Column(db.BigInteger, db.ForeignKey(Index.id, ondelete='CASCADE'), nullable=False) # """ID of Index to whom this shib user belongs.""" index = db.relationship(Index, backref=db.backref('journal', cascade='all'), cascade='all, delete-orphan', single_parent=True) """ID of Index to whom this journal belongs.""" publication_title = db.Column(db.Text, nullable=True, default='') """Title of the journal.""" print_identifier = db.Column(db.Text, nullable=True, default='') """Print-format identifier of the journal.""" """ varchar(20) It complies with the format of ISBN or ISSN. ISSN: "^\\d{4}-?\\d{3}[0-9X]$" ISBN: "^\\d{9}[0-9X]$" "^\\d+-\\d+-\\d+-[0-9X]$" "^97[8-9]\\d{9}[0-9X]$" "^97[8-9]-\\d+-\\d+-\\d+-[0-9X]$" """ online_identifier = db.Column(db.Text, nullable=True, default='') """Online-format identifier of the journal.""" """ varchar(20) It complies with the format of ISBN or ISSN. ISSN: "^\\d{4}-?\\d{3}[0-9X]$" ISBN: "^\\d{9}[0-9X]$" "^\\d+-\\d+-\\d+-[0-9X]$" "^97[8-9]\\d{9}[0-9X]$" "^97[8-9]-\\d+-\\d+-\\d+-[0-9X]$" """ date_first_issue_online = db.Column(db.Text, nullable=True, default='') """Date of first issue available online of the journal.""" """ varchar(10) It has one of the following format. YYYY-MM-DD and current date YYYY-MM YYYY YYYY is can be input only from 1700-2030 """ num_first_vol_online = db.Column(db.Text, nullable=True, default='') """Number of first volume available online of the journal.""" """ varchar(255) """ num_first_issue_online = db.Column(db.Text, nullable=True, default='') """Number of first issue available online of the journal.""" """ varchar(255) """ date_last_issue_online = db.Column(db.Text, nullable=True, default='') """Date of last issue available online of the journal.""" """ varchar(10) It has one of the following format. YYYY-MM-DD and current date YYYY-MM YYYY YYYY is can be input only from 1700-2030 """ num_last_vol_online = db.Column(db.Text, nullable=True, default='') """Number of last volume available online of the journal.""" """ varchar(255) """ num_last_issue_online = db.Column(db.Text, nullable=True, default='') """Number of last issue available online of the journal.""" """ varchar(255) """ title_url = db.Column(db.Text, nullable=True, default='') """ varchar(2048) WEKO index search result page display URL [Top Page URL] /? Action = repository_opensearch & index_id = [title_id] """ first_author = db.Column(db.Text, nullable=True, default='') """ first_author """ title_id = db.Column(db.BigInteger, nullable=True, default=0) """ Output the index ID of WEKO. """ embargo_info = db.Column(db.Text, nullable=True, default='') """Embargo information of the journal.""" """ varchar(255) """ coverage_depth = db.Column(db.Text, nullable=True, default='') """Coverage depth of the journal.""" """ varchar(255) Select one of the following items: Abstract, Fulltext, Selected Articles """ coverage_notes = db.Column(db.Text, nullable=True, default='') """Coverage notes of the journal.""" """ varchar(255) """ publisher_name = db.Column(db.Text, nullable=True, default='') """The Publisher name of the journal.""" """ varchar(255) """ publication_type = db.Column(db.Text, nullable=True, default='') """Publication type of the journal.""" """ varchar(255) Select the following item: "Serial" """ date_monograph_published_print = db.Column(db.Text, nullable=True, default='') """" date_monograph_published_print """ date_monograph_published_online = db.Column(db.Text, nullable=True, default='') """" date_monograph_published_online """ monograph_volume = db.Column(db.Text, nullable=True, default='') """" monograph_volume """ monograph_edition = db.Column(db.Text, nullable=True, default='') """" monograph_edition """ first_editor = db.Column(db.Text, nullable=True, default='') """" first_editor """ parent_publication_title_id = db.Column(db.BigInteger, nullable=True, default=0) """Parent publication identifier of the journal.""" """ int(11) An integer of 1 or larger. It's the index ID of index containing journal information """ preceding_publication_title_id = db.Column(db.BigInteger, nullable=True, default=0) """Preceding publication identifier of the journal.""" """ int(11) An integer of 1 or larger. It's the index ID of index containing journal information """ access_type = db.Column(db.Text, nullable=True, default='') """Access type of the journal.""" """ varchar(1) Select 1 of the following items: F, P Initial selection is "F" Describe the following in item name of WEKO when registering journal information. F:Free P:Paid """ language = db.Column(db.Text, nullable=True, default='') """Language of the journal.""" """ varchar(7) Select from language code (ISO639-2). In the pulldown, show: jpn, eng, chi, kor, (the others language by alphabet order). """ title_alternative = db.Column(db.Text, nullable=True, default='') """Title alternative of the journal.""" """ varchar(255) """ title_transcription = db.Column(db.Text, nullable=True, default='') """Title transcription of the journal.""" """ varchar(255) """ ncid = db.Column(db.Text, nullable=True, default='') """NCID of the journal.""" """ varchar(10) Allow the followoing formats. "^[AB][ABN][0-9]{7}[0-9X]$" """ ndl_callno = db.Column(db.Text, nullable=True, default='') """NDL Call No. of the journal.""" """ varchar(20) Half-size alphanumeric symbol within 20 characters """ ndl_bibid = db.Column(db.Text, nullable=True, default='') """NDL Call No. of the journal.""" """ varchar(20) Half-size alphanumeric symbol within 20 characters TODO: Need to repair. """ jstage_code = db.Column(db.Text, nullable=True, default='') """J-STAGE CDJOURNAL of the journal.""" """ varchar(20) Half-size alphanumeric symbol within 20 characters """ ichushi_code = db.Column(db.Text, nullable=True, default='') """Ichushi Code of the journal.""" """ varchar(6) Allow the following input:"^J[0-9]{5}$" """ deleted = db.Column(db.Text, nullable=True, default='') """Always output with empty string (character string length = 0)""" is_output = db.Column(db.Boolean(name='is_output'), nullable=True, default=lambda: False) owner_user_id = db.Column(db.Integer, nullable=True, default=0) """Owner user id of the journal.""" def __iter__(self): """Yield distributions for non-duplicate projects in the working set. The yield order is the order in which the items' path entries were added to the working set. """ for name in dir(Journal): if not name.startswith('__') and not name.startswith('_') \ and name not in dir(Timestamp): value = getattr(self, name) if value is None: value = "" if isinstance(value, str) or isinstance(value, bool) \ or isinstance(value, datetime) \ or isinstance(value, int): yield (name, value) # format setting for community admin page def __str__(self): """Representation.""" return 'Journal <id={0.id}, index_name={0.publication_title}>'.format( self)
class ItemTypeProperty(db.Model, Timestamp): """Represent an itemtype property. The ItemTypeProperty object contains a ``created`` and a ``updated`` properties that are automatically updated. """ __tablename__ = 'item_type_property' id = db.Column(db.Integer(), primary_key=True, autoincrement=True) """Identifier of itemtype property.""" name = db.Column(db.Text, nullable=False, unique=True) """Name identifier of itemtype property.""" schema = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store schema in JSON format. When you create a new ``ItemTypeProperty`` the ``schema`` field value should never be ``NULL``. Default value is an empty dict. ``NULL`` value means that the record metadata has been deleted. """ form = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store schema form (single) in JSON format. When you create a new ``ItemTypeProperty`` the ``form`` field value should never be ``NULL``. Default value is an empty dict. ``NULL`` value means that the record metadata has been deleted. """ forms = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """Store schema form (array) in JSON format. When you create a new ``ItemTypeProperty`` the ``forms`` field value should never be ``NULL``. Default value is an empty dict. ``NULL`` value means that the record metadata has been deleted. """ delflg = db.Column(db.Boolean(name='delFlg'), default=False, nullable=False) """record delete flag""" sort = db.Column(db.Integer, nullable=True, unique=True) """Sort number of itemtype property."""
class OAIServerSchema(db.Model, Timestamp): """Represent a OAIServer Schema. The OAIServer object contains a ``created`` and a ``updated`` properties that are automatically updated. """ # Enables SQLAlchemy-Continuum versioning __versioned__ = {} __tablename__ = 'oaiserver_schema' id = db.Column( UUIDType, primary_key=True, default=uuid.uuid4, ) """schema identifier.""" schema_name = db.Column( db.String(255), nullable=False, unique=True ) """Mapping Name of schema""" form_data = db.Column( db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True ) """Data(schema name,root name,description) of form.""" xsd = db.Column( db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: OrderedDict(), nullable=False ) """Xsd schema""" namespaces = db.Column( db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True ) """NameSpace for xml""" schema_location = db.Column( db.String(255) ) """Schema Url""" isvalid = db.Column( db.Boolean(name='isvalid'), nullable=False, default=lambda: False ) is_mapping = db.Column( db.Boolean(name='is_mapping'), nullable=False, default=lambda: False ) isfixed = db.Column( db.Boolean(name='isfixed'), nullable=False, default=lambda: False ) version_id = db.Column(db.Integer, nullable=False) """Used by SQLAlchemy for optimistic concurrency control.""" target_namespace = db.Column(db.String(255), default='') __mapper_args__ = { 'version_id_col': version_id }
class ActionNeedMixin(object): """Define common attributes for Action needs.""" id = db.Column(db.Integer, autoincrement=True, primary_key=True) """Primary key. It allows the other fields to be nullable.""" action = db.Column(db.String(80), index=True) """Name of the action.""" exclude = db.Column(db.Boolean(name='exclude'), nullable=False, default=False, server_default='0') """If set to True, deny the action, otherwise allow it.""" argument = db.Column(db.String(255), nullable=True, index=True) """Action argument.""" @classmethod def create(cls, action, **kwargs): """Create new database row using the provided action need. :param action: An object containing a method equal to ``'action'`` and a value. :param argument: The action argument. If this parameter is not passed, then the ``action.argument`` will be used instead. If the ``action.argument`` does not exist, ``None`` will be set as argument for the new action need. :returns: An :class:`invenio_access.models.ActionNeedMixin` instance. """ assert action.method == 'action' argument = kwargs.pop('argument', None) or getattr( action, 'argument', None) return cls(action=action.value, argument=argument, **kwargs) @classmethod def allow(cls, action, **kwargs): """Allow the given action need. :param action: The action to allow. :returns: A :class:`invenio_access.models.ActionNeedMixin` instance. """ return cls.create(action, exclude=False, **kwargs) @classmethod def deny(cls, action, **kwargs): """Deny the given action need. :param action: The action to deny. :returns: A :class:`invenio_access.models.ActionNeedMixin` instance. """ return cls.create(action, exclude=True, **kwargs) @classmethod def query_by_action(cls, action, argument=None): """Prepare query object with filtered action. :param action: The action to deny. :param argument: The action argument. If it's ``None`` then, if exists, the ``action.argument`` will be taken. In the worst case will be set as ``None``. (Default: ``None``) :returns: A query object. """ query = cls.query.filter_by(action=action.value) argument = argument or getattr(action, 'argument', None) if argument is not None: query = query.filter( db.or_( cls.argument == str(argument), cls.argument.is_(None), )) else: query = query.filter(cls.argument.is_(None)) return query @property def need(self): """Return the need corresponding to this model instance. This is an abstract method and will raise NotImplementedError. """ raise NotImplementedError() # pragma: no cover
class ResyncIndexes(db.Model, Timestamp): """ResyncIndexes model. Stores session life_time created for Session. """ __tablename__ = 'resync_indexes' id = db.Column(db.Integer, primary_key=True, autoincrement=True) """Identifier of resource list.""" status = db.Column(db.String(), nullable=False, default=lambda: current_app.config[ 'INVENIO_RESYNC_INDEXES_STATUS'].get('automatic')) """Status of resource list.""" index_id = db.Column(db.BigInteger, db.ForeignKey(Index.id, ondelete='CASCADE'), nullable=True) """Index Identifier relation to resync indexes.""" repository_name = db.Column(db.String(50), nullable=False) """Repository name.""" from_date = db.Column(db.DateTime, nullable=True) """From Date.""" to_date = db.Column(db.DateTime, nullable=True) """To Date.""" resync_save_dir = db.Column(db.String(50), nullable=False) """Path directory save.""" resync_mode = db.Column(db.String(20), nullable=False, default=lambda: current_app.config[ 'INVENIO_RESYNC_INDEXES_MODE'].get('baseline')) """Resync mode.""" saving_format = db.Column( db.String(10), nullable=False, default=lambda: current_app.config[ 'INVENIO_RESYNC_INDEXES_SAVING_FORMAT'].get('jpcoar')) """Saving format.""" base_url = db.Column(db.String(255), nullable=False) """base url of resync.""" is_running = db.Column(db.Boolean(), default=True) """is running.""" interval_by_day = db.Column(db.Integer, nullable=False) """Time cycle for each change list.""" task_id = db.Column(db.String(40), default=None) result = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) index = db.relationship(Index, backref='resync_index_id', foreign_keys=[index_id]) """Relation to the Index Identifier."""
class Index(db.Model, Timestamp): """ Represent an index. The Index object contains a ``created`` and a ``updated`` properties that are automatically updated. """ __tablename__ = 'index' __table_args__ = (db.UniqueConstraint('parent', 'position', name='uix_position'), ) id = db.Column(db.BigInteger, primary_key=True, unique=True) """Identifier of the index.""" parent = db.Column(db.BigInteger, nullable=False, default=0) """Parent Information of the index.""" position = db.Column(db.Integer, nullable=False, default=0) """Children position of parent.""" index_name = db.Column(db.Text, nullable=True, default='') """Name of the index.""" index_name_english = db.Column(db.Text, nullable=False, default='') """English Name of the index.""" index_link_name = db.Column(db.Text, nullable=True, default='') """Name of the index link.""" index_link_name_english = db.Column(db.Text, nullable=False, default='') """English Name of the index link.""" harvest_spec = db.Column(db.Text, nullable=True, default='') """Harvest Spec.""" index_link_enabled = db.Column(db.Boolean(name='index_link_enabled'), nullable=False, default=False) """Index link enable flag.""" comment = db.Column(db.Text, nullable=True, default='') """Comment of the index.""" more_check = db.Column(db.Boolean(name='more_check'), nullable=False, default=False) """More Status of the index.""" display_no = db.Column(db.Integer, nullable=False, default=0) """Display number of the index.""" harvest_public_state = db.Column(db.Boolean(name='harvest_public_state'), nullable=False, default=True) """Harvest public State of the index.""" display_format = db.Column(db.Text, nullable=True, default='1') """The Format of Search Resault.""" image_name = db.Column(db.Text, nullable=False, default='') """The Name of upload image.""" public_state = db.Column(db.Boolean(name='public_state'), nullable=False, default=False) """Public State of the index.""" public_date = db.Column(db.DateTime().with_variant(mysql.DATETIME(fsp=6), "mysql"), nullable=True) """Public Date of the index.""" recursive_public_state = db.Column(db.Boolean(name='recs_public_state'), nullable=True, default=False) """Recursive Public State of the index.""" coverpage_state = db.Column(db.Boolean(name='coverpage_state'), nullable=True, default=False) """PDF Cover Page State of the index.""" recursive_coverpage_check = db.Column( db.Boolean(name='recursive_coverpage_check'), nullable=True, default=False) """Recursive PDF Cover Page State of the index.""" browsing_role = db.Column(db.Text, nullable=True) """Browsing Role of the .""" recursive_browsing_role = db.Column(db.Boolean(name='recs_browsing_role'), nullable=True, default=False) """Recursive Browsing Role of the index.""" contribute_role = db.Column(db.Text, nullable=True) """Contribute Role of the index.""" recursive_contribute_role = db.Column( db.Boolean(name='recs_contribute_role'), nullable=True, default=False) """Recursive Browsing Role of the index.""" browsing_group = db.Column(db.Text, nullable=True) """Browsing Group of the .""" recursive_browsing_group = db.Column( db.Boolean(name='recs_browsing_group'), nullable=True, default=False) """Recursive Browsing Group of the index.""" contribute_group = db.Column(db.Text, nullable=True) """Contribute Group of the index.""" recursive_contribute_group = db.Column( db.Boolean(name='recs_contribute_group'), nullable=True, default=False) """Recursive Browsing Group of the index.""" owner_user_id = db.Column(db.Integer, nullable=True, default=0) """Owner user id of the index.""" # item_custom_sort = db.Column(db.Text, nullable=True, default='') item_custom_sort = db.Column(db.JSON().with_variant( postgresql.JSONB(none_as_null=True), 'postgresql', ).with_variant( JSONType(), 'sqlite', ).with_variant( JSONType(), 'mysql', ), default=lambda: dict(), nullable=True) """The sort of item by custom setting""" # index_items = db.relationship('IndexItems', back_populates='index', cascade='delete') def __iter__(self): """Iter.""" for name in dir(Index): if not name.startswith('__') and not name.startswith('_') \ and name not in dir(Timestamp): value = getattr(self, name) if value is None: value = "" if isinstance(value, str) or isinstance(value, bool) \ or isinstance(value, datetime) \ or isinstance(value, int): yield (name, value) # format setting for community admin page def __str__(self): """Representation.""" return 'Index <id={0.id}, index_name={0.index_name_english}>'.format( self) @classmethod def have_children(cls, id): """Have Children.""" children = cls.query.filter_by(parent=id).all() return False if (children is None or len(children) == 0) else True
class SIP(db.Model, Timestamp): """Submission Information Package model.""" __tablename__ = 'sipstore_sip' id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4) """Id of SIP.""" user_id = db.Column(db.Integer, db.ForeignKey(User.id, name='fk_sipstore_sip_user_id'), nullable=True, default=None) """User responsible for the SIP.""" agent = db.Column(JSONType, default=lambda: dict(), nullable=False) """Agent information regarding given SIP.""" archivable = db.Column(db.Boolean(name='archivable'), nullable=False, default=True) """Boolean stating if the SIP should be archived or not.""" archived = db.Column(db.Boolean(name='archived'), nullable=False, default=False) """Boolean stating if the SIP has been archived or not.""" # # Relationships # user = db.relationship(User, backref='sips', foreign_keys=[user_id]) """Relation to the User responsible for the SIP.""" @classmethod def create(cls, user_id=None, agent=None, id_=None, archivable=True, archived=False): """Create a Submission Information Package object. :param user_id: Id of the user responsible for the SIP. :type user_id: int :param agent: Extra information on submitting agent in JSON format. :type agent: dict :param bool archivable: Tells if the SIP should be archived or not. :param bool archived: Tells if the SIP has been archived. """ if user_id and (User.query.get(user_id) is None): raise SIPUserDoesNotExist(user_id) agent = agent or dict() if current_app.config['SIPSTORE_AGENT_JSONSCHEMA_ENABLED']: agent.setdefault( '$schema', current_jsonschemas.path_to_url( current_app.config['SIPSTORE_DEFAULT_AGENT_JSONSCHEMA'])) schema_path = current_jsonschemas.url_to_path(agent['$schema']) if not schema_path: raise JSONSchemaNotFound(agent['$schema']) schema = current_jsonschemas.get_schema(schema_path) validate(agent, schema) with db.session.begin_nested(): obj = cls(id=id_ or uuid.uuid4(), user_id=user_id, agent=agent, archivable=archivable, archived=archived) db.session.add(obj) return obj
class Banner(db.Model, Timestamp): """Defines a message to show to users.""" __tablename__ = "banners" __versioned__ = {"versioning": False} id = db.Column(db.Integer, primary_key=True) message = db.Column(db.Text, nullable=False) """The message content.""" url_path = db.Column(db.String(255), nullable=True) """Define in which URL /path the message will be visible.""" category = db.Column(db.String(20), nullable=False) """Category of the message, for styling messages per category.""" start_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) """Start date and time (UTC), can be immediate or delayed.""" end_datetime = db.Column(db.DateTime, nullable=True) """End date and time (UTC), must be after `start` or forever if null.""" active = db.Column(db.Boolean(name="active"), nullable=False, default=True) """Defines if the message is active, only one at the same time.""" @classmethod def create( cls, message, category, url_path=None, start_datetime=None, end_datetime=None, active=False, ): """Create a new banner.""" _categories = [t[0] for t in current_app.config["BANNERS_CATEGORIES"]] assert category in _categories with db.session.begin_nested(): obj = cls( message=message, category=category, url_path=url_path, start_datetime=start_datetime, end_datetime=end_datetime, active=active, ) db.session.add(obj) return obj @classmethod def get_active(cls, url_path=None): """Return the active banner, optionally for the given /path or None.""" url_path = url_path or "" now = datetime.utcnow() query = (cls.query.filter(cls.active.is_(True)).filter( cls.start_datetime <= now).filter((cls.end_datetime.is_(None)) | (now <= cls.end_datetime))) for banner in query.all(): if banner.url_path is None or url_path.startswith(banner.url_path): return banner return None @classmethod def disable_expired(cls): """Disable any old still active messages to keep everything clean.""" now = datetime.utcnow() query = (cls.query.filter(cls.active.is_(True)).filter( cls.end_datetime.isnot(None)).filter(cls.end_datetime < now)) for old in query.all(): old.active = False db.session.commit()
class Bucket(db.Model, Timestamp): """Model for storing buckets. A bucket is a container of objects. Buckets have a default location and storage class. Individual objects in the bucket can however have different locations and storage classes. A bucket can be marked as deleted. A bucket can also be marked as locked to prevent operations on the bucket. Each bucket can also define a quota. The size of a bucket is the size of all objects in the bucket (including all versions). """ __tablename__ = 'files_bucket' id = db.Column( UUIDType, primary_key=True, default=uuid.uuid4, ) """Bucket identifier.""" default_location = db.Column(db.Integer, db.ForeignKey(Location.id, ondelete='RESTRICT'), nullable=False) """Default location.""" default_storage_class = db.Column( db.String(1), nullable=False, default=lambda: current_app.config['FILES_REST_DEFAULT_STORAGE_CLASS']) """Default storage class.""" size = db.Column(db.BigInteger, default=0, nullable=False) """Size of bucket. This is a computed property which can rebuilt any time from the objects inside the bucket. """ quota_size = db.Column( db.BigInteger, nullable=True, default=lambda: current_app.config['FILES_REST_DEFAULT_QUOTA_SIZE']) """Quota size of bucket. Usage of this property depends on which file size limiters are installed. """ max_file_size = db.Column( db.BigInteger, nullable=True, default=lambda: current_app.config['FILES_REST_DEFAULT_MAX_FILE_SIZE']) """Maximum size of a single file in the bucket. Usage of this property depends on which file size limiters are installed. """ locked = db.Column(db.Boolean(name='locked'), default=False, nullable=False) """Lock state of bucket. Modifications are not allowed on a locked bucket. """ deleted = db.Column(db.Boolean(name='deleted'), default=False, nullable=False) """Delete state of bucket.""" location = db.relationship(Location, backref='buckets') """Location associated with this bucket.""" def __repr__(self): """Return representation of location.""" return str(self.id) @property def quota_left(self): """Get how much space is left in the bucket.""" if self.quota_size: return max(self.quota_size - self.size, 0) @property def size_limit(self): """Get size limit for this bucket. The limit is based on the minimum output of the file size limiters. """ limits = [ lim for lim in current_files_rest.file_size_limiters(self) if lim.limit is not None ] return min(limits) if limits else None @validates('default_storage_class') def validate_storage_class(self, key, default_storage_class): """Validate storage class.""" if default_storage_class not in \ current_app.config['FILES_REST_STORAGE_CLASS_LIST']: raise ValueError('Invalid storage class.') return default_storage_class @ensure_not_deleted() def snapshot(self, lock=False): """Create a snapshot of latest objects in bucket. :param lock: Create the new bucket in a locked state. :returns: Newly created bucket with the snapshot. """ with db.session.begin_nested(): b = Bucket( default_location=self.default_location, default_storage_class=self.default_storage_class, quota_size=self.quota_size, ) db.session.add(b) for o in ObjectVersion.get_by_bucket(self): o.copy(bucket=b) b.locked = True if lock else self.locked return b def get_tags(self): """Get tags for bucket as dictionary.""" return {t.key: t.value for t in self.tags} @classmethod def create(cls, location=None, storage_class=None, **kwargs): r"""Create a bucket. :param location: Location of bucket (instance or name). Default: Default location. :param storage_class: Storage class of bucket. Default: Default storage class. :param \**kwargs: Keyword arguments are forwarded to the class constructor. :returns: Created bucket. """ with db.session.begin_nested(): if location is None: location = Location.get_default() elif isinstance(location, six.string_types): location = Location.get_by_name(location) obj = cls( default_location=location.id, default_storage_class=storage_class or current_app.config['FILES_REST_DEFAULT_STORAGE_CLASS'], **kwargs) db.session.add(obj) return obj @classmethod def get(cls, bucket_id): """Get bucket object (excluding deleted). :param bucket_id: Bucket identifier. :returns: Bucket instance. """ return cls.query.filter_by(id=bucket_id, deleted=False).one_or_none() @classmethod def all(cls): """Return query of all buckets (excluding deleted).""" return cls.query.filter_by(deleted=False) @classmethod def delete(cls, bucket_id): """Delete a bucket. Does not actually delete the Bucket, just marks it as deleted. """ bucket = cls.get(bucket_id) if not bucket or bucket.deleted: return False bucket.deleted = True return True @ensure_unlocked() def remove(self): """Permanently remove a bucket and all objects (including versions). .. warning:: This by-passes the normal versioning and should only be used when you want to permanently delete a bucket and its objects. Otherwise use :py:data:`Bucket.delete()`. Note the method does not remove the associated file instances which must be garbage collected. :returns: ``self``. """ with db.session.begin_nested(): ObjectVersion.query.filter_by(bucket_id=self.id).delete() self.query.filter_by(id=self.id).delete() return self