예제 #1
0
class OrgsMembers(db.Model):  # type: ignore
    __bind_key__ = 'dashboard_service'
    __tablename__ = "orgs_members"
    org_id = db.Column(UUIDType(binary=False),
                       db.ForeignKey('org.id'),
                       primary_key=True)
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('user_account.id'),
                           primary_key=True)
    is_owner = db.Column(db.Boolean(), default=False)
    account = db.relationship('UserAccount')
    organization = db.relationship('Org')
예제 #2
0
class WorkpacesMembers(db.Model):  # type: ignore
    __bind_key__ = 'dashboard_service'
    __tablename__ = "workspaces_members"
    workspace_id = db.Column(UUIDType(binary=False),
                             db.ForeignKey('workspace.id'),
                             primary_key=True)
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('user_account.id'),
                           primary_key=True)
    is_owner = db.Column(db.Boolean(), default=False)
    account = db.relationship('UserAccount')
    workspace = db.relationship('Workspace')
예제 #3
0
class UserAccount(db.Model):  # type: ignore
    __bind_key__ = 'dashboard_service'
    __tablename__ = 'user_account'
    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    joined_dt = db.Column(db.DateTime(), server_default=func.now())
    closed_dt = db.Column(db.DateTime())
    inactive_dt = db.Column(db.DateTime())
    is_closed = db.Column(db.Boolean, default=False)
    is_active = db.Column(db.Boolean, default=True)
    info = db.relationship('UserInfo',
                           backref='account',
                           uselist=False,
                           cascade="all, delete-orphan")
    privacy = db.relationship('UserPrivacy',
                              backref='account',
                              uselist=False,
                              cascade="all, delete-orphan")
    workspaces = db.relationship('Workspace',
                                 secondary="workspaces_members",
                                 lazy='subquery',
                                 backref=db.backref('accounts', lazy=True))
    orgs = db.relationship('Org',
                           secondary="orgs_members",
                           lazy='subquery',
                           backref=db.backref('accounts', lazy=True))
    # direct access to the unique personal org
    # but this org is also part of the many to many relationship
    personal_org = db.relationship('Org',
                                   backref='account',
                                   uselist=False,
                                   cascade="all, delete-orphan")

    def to_public_dict(self):
        return {
            "id": shortuuid.encode(self.id),
            "joined": "{}Z".format(self.joined_dt.isoformat()),
            "workspaces": [w.to_dict() for w in self.workspaces],
            "orgs": [o.to_dict() for o in self.orgs]
        }

    def to_short_dict(self):
        return {
            "id": shortuuid.encode(self.id),
            "joined": "{}Z".format(self.joined_dt.isoformat()),
            "org": {
                "name": self.personal_org.name
            },
            "profile": self.info.to_public_dict()
        }
예제 #4
0
class Recommendation(db.Model):  # type: ignore
    __bind_key__ = 'experiment_service'
    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    recommendation_type = db.Column(db.String(), index=True)
    tags = db.relationship('RecommendationTag',
                           secondary="recommendation_tags_assoc",
                           backref=db.backref('recommendations', lazy=True))
    checksum = db.Column(db.String(), nullable=False)
    last_updated = db.Column(db.DateTime())
    rating = db.Column(db.Float, default=3.0)
    meta = db.Column(JSONB())
    data = db.Column(JSONB(), nullable=False)

    def to_dict(self):
        data = self.data
        if self.recommendation_type == "experiment":
            data = data["hashed"]
        return {
            "id": str(self.id),
            "type": self.recommendation_type,
            "tags": [tag.value for tag in self.tags],
            "checksum": self.checksum,
            "last_updated": "{}Z".format(self.last_updated.isoformat()),
            "meta": self.meta,
            "data": data
        }
예제 #5
0
class ProviderToken(db.Model, OAuth2TokenMixin):  # type: ignore
    __bind_key__ = 'auth_service'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('account.id', ondelete='CASCADE'))
    account = db.relationship('Account')

    def to_dict(self):
        return {
            "id": self.id,
            "access_token": self.access_token,
            "token_type": self.token_type,
            "refresh_token": self.refresh_token,
            "expires_at": self.expires_in,
            "revoked": self.revoked
        }
예제 #6
0
class Account(db.Model):  # type: ignore
    __bind_key__ = 'auth_service'
    __table_args__ = (db.UniqueConstraint('oauth_provider',
                                          'oauth_provider_sub',
                                          name='oauth_provider_sub_uniq'), )

    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   unique=True,
                   default=uuid.uuid4)
    joined_on = db.Column(db.DateTime(timezone=True),
                          server_default=func.now(),
                          nullable=False)
    closed_since = db.Column(db.DateTime(timezone=True), nullable=True)
    inactive_since = db.Column(db.DateTime(timezone=True), nullable=True)
    is_closed = db.Column(db.Boolean, default=False)
    is_active = db.Column(db.Boolean, default=True)
    oauth_provider = db.Column(db.String, nullable=True)
    oauth_provider_sub = db.Column(db.String, nullable=True)

    access_tokens = db.relationship('AccessToken',
                                    backref='account',
                                    cascade="all, delete-orphan")
    client = db.relationship('Client',
                             backref='account',
                             uselist=False,
                             cascade="all, delete-orphan")
    local = db.relationship('LocalAccount',
                            backref='account',
                            uselist=False,
                            cascade="all, delete-orphan")

    def to_dict(self):
        inactive = closed = None
        if self.inactive_since:
            inactive = "{}Z".format(self.inactive_since.isoformat())
        if self.closed_since:
            closed = "{}Z".format(self.closed_since.isoformat())

        return {
            "id": str(self.id),
            "short_id": shortuuid.encode(self.id),
            "closed": self.is_closed,
            "active": self.is_active,
            "joined_on": "{}Z".format(self.joined_on.isoformat()),
            "inactive_since": inactive,
            "closed_since": closed,
            "client": self.client.to_dict() if self.client else None,
            "tokens": [t.to_dict() for t in self.access_tokens]
        }

    def turn_inactive(self):
        self.is_active = False
        self.inactive_since = datetime.utcnow()

    def turn_active(self):
        self.is_active = True
        self.inactive_since = None

    def close_account(self):
        self.is_closed = False
        self.closed_since = datetime.utcnow()
예제 #7
0
class Experiment(db.Model):  # type: ignore
    __bind_key__ = 'experiment_service'
    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    shared_ref = db.Column(UUIDType(binary=False), nullable=False, index=True)
    account_id = db.Column(UUIDType(binary=False), nullable=False, index=True)
    created_date = db.Column(db.DateTime(), server_default=func.now())
    updated_date = db.Column(db.DateTime(),
                             server_default=func.now(),
                             server_onupdate=func.now())
    suggested_experiment_id = db.Column(UUIDType(binary=False), index=True)
    org_id = db.Column(UUIDType(binary=False), nullable=False, index=True)
    workspace_id = db.Column(UUIDType(binary=False),
                             nullable=False,
                             index=True)
    executions = db.relationship('Execution',
                                 backref='experiment',
                                 cascade="all, delete-orphan")
    payload = db.Column(NestedMutable.as_mutable(JSONB), nullable=False)

    def to_dict(self, with_payload: bool = True):
        updated_date = None
        timestamp = self.created_date.timestamp()
        if self.updated_date:
            updated_date = "{}Z".format(self.updated_date.isoformat())
            timestamp = self.updated_date.timestamp()

        d = {
            "id": shortuuid.encode(self.id),
            "ref": shortuuid.encode(self.shared_ref),
            "created_date": "{}Z".format(self.created_date.isoformat()),
            "updated_date": updated_date,
            "timestamp": timestamp,
            "org": shortuuid.encode(self.org_id),
            "workspace": shortuuid.encode(self.workspace_id),
            "title": self.payload.get("title"),
            "description": self.payload.get("description")
        }

        if with_payload:
            d["payload"] = self.payload

        return d

    def to_public_dict(self, with_payload: bool = True):
        updated_date = None
        timestamp = self.created_date.timestamp()
        if self.updated_date:
            updated_date = "{}Z".format(self.updated_date.isoformat())
            timestamp = self.updated_date.timestamp()

        d = {
            "id": shortuuid.encode(self.id),
            "ref": shortuuid.encode(self.shared_ref),
            "created_date": "{}Z".format(self.created_date.isoformat()),
            "updated_date": updated_date,
            "timestamp": timestamp,
            "org": shortuuid.encode(self.org_id),
            "workspace": shortuuid.encode(self.workspace_id),
            "title": self.payload.get("title"),
            "description": self.payload.get("description"),
            "tags": [tag for tag in self.payload.get("tags", [])]
        }

        if with_payload:
            d["payload"] = self.payload

        return d

    @staticmethod
    def get_by_id(exp_id: Union[str, uuid.UUID]) -> Optional['Experiment']:
        if not exp_id:
            return None

        if isinstance(exp_id, str):
            try:
                exp_id = shortuuid.decode(exp_id)
            except ValueError:
                return None

        return Experiment.query.filter(Experiment.id == exp_id).first()

    def get_execution(self, timestamp: int) -> Optional['Execution']:
        return Execution.query.filter(
            Execution.experiment_id == self.id,
            Execution.timestamp == timestamp).first()
예제 #8
0
class Org(db.Model):  # type: ignore
    __bind_key__ = 'dashboard_service'

    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    # only set when this is a personal org linked to a single account,
    # otherwise it's not set
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('user_account.id'),
                           nullable=True)
    name = db.Column(db.String(), nullable=False, unique=True)
    name_lower = db.Column(db.String(), nullable=False, unique=True)
    kind = db.Column(db.Enum(OrgType),
                     nullable=False,
                     default=OrgType.personal)
    created_on = db.Column(db.DateTime(), server_default=func.now())
    workspaces = db.relationship('Workspace',
                                 backref='org',
                                 cascade="all, delete-orphan")
    settings = db.Column(JSONB(), nullable=False, default=DEFAULT_ORG_SETTINGS)

    def to_dict(self, public_workspaces_only: bool = False):
        workspaces = []
        for w in self.workspaces:
            if public_workspaces_only and w.kind != WorkspaceType.public:
                continue

            workspaces.append({"id": shortuuid.encode(w.id), "name": w.name})

        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "settings": self.settings,
            "type": self.kind.value,
            "created_on": "{}Z".format(self.created_on.isoformat()),
            "workspaces": workspaces
        }

    def to_short_dict(self):
        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "created_on": "{}Z".format(self.created_on.isoformat()),
            "settings": self.settings,
            "type": self.kind.value
        }

    @staticmethod
    def get_next_available_name(suggested_name: str) -> str:
        """
        Return the next available name prefixed by the given suggested name and
        suffixed by a number between 0 and 1000.

        If `suggested_name` is not used yet, return it as the available name
        """
        while True:
            has_org = Org.query.filter(Org.name == suggested_name).first()
            if not has_org:
                return suggested_name
            suggested_name = "{}{}".format(suggested_name,
                                           secrets.randbelow(1000))

    @staticmethod
    def get_by_id(org_id: Union[str, uuid.UUID]) -> 'Org':
        """
        Lookup an organization by its identifier
        """
        return Org.query.filter(Org.id == org_id).first()

    @staticmethod
    def find_by_name(org_name: str) -> 'Org':
        """
        Lookup an organization by its name
        """
        return Org.query.filter(Org.name_lower == org_name.lower()).first()

    def find_workspace_by_name(self,
                               workspace_name: str) -> Optional[Workspace]:
        """
        Lookup a workspace in the organization by its name
        """
        w_name = workspace_name.lower()
        for workspace in self.workspaces:
            if workspace.name_lower == w_name:
                return workspace

        return None

    def is_member(self, account_id: Union[str, uuid.UUID]) -> bool:
        """
        Return `True` when the given account is a member of the organization
        """
        return OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).first() is not None

    def is_owner(self, account_id: Union[str, uuid.UUID]) -> bool:
        """
        Return `True` when the given account is an owner of the organization
        """
        return OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id, OrgsMembers.is_owner == True,
            OrgsMembers.account_id == account_id).first() is not None

    def has_single_owner(self) -> bool:
        """
        Return `True` if only one owner exists for this organization
        """
        return OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.is_owner == True).count() == 1

    def make_member(self, account_id: Union[str, uuid.UUID]):
        """
        Turn an user as a member only of this organization.

        The user must already be part of this organization, this is mostly
        therefore useful when moving an owner down to simple member.
        """
        membership = OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).first()
        if membership:
            membership.is_owner = False

    def make_owner(self, account_id: Union[str, uuid.UUID]):
        """
        Turn an user as an owner of this organization.
        """
        membership = OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).first()
        if membership:
            membership.is_owner = True

    def add_member(self, account_id: Union[str, uuid.UUID]) -> OrgsMembers:
        """
        Add this user to the organization as a member
        """
        membership = OrgsMembers(org_id=self.id, account_id=account_id)
        db.session.add(membership)
        return membership

    def remove_member(self, account_id: Union[str, uuid.UUID]):
        """
        Remove this member from the organization
        """
        OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).delete()

    def get_public_workspace_ids(self) -> List[uuid.UUID]:
        """
        List all public workspaces in this organization and return their
        identifiers
        """
        result = db.session.query(Workspace.id).filter(
            Workspace.org_id == self.id,
            Workspace.kind == WorkspaceType.public).all()
        if not result:
            return []

        return result[0]