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')
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')
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() }
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 }
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 }
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()
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()
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]