class RecommendationTagsAssoc(db.Model): # type: ignore __bind_key__ = 'experiment_service' __tablename__ = "recommendation_tags_assoc" recommendation_id = db.Column(UUIDType, db.ForeignKey('recommendation.id'), primary_key=True) tag_id = db.Column(db.Integer, db.ForeignKey('recommendation_tag.id'), primary_key=True)
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 AccessToken(db.Model, OAuth2TokenMixin): # type: ignore __bind_key__ = 'auth_service' id = db.Column(UUIDType(binary=False), primary_key=True, unique=True, default=uuid.uuid4) name = db.Column(db.String(), nullable=False) account_id = db.Column(UUIDType(binary=False), db.ForeignKey('account.id', ondelete='CASCADE'), nullable=False) last_used_on = db.Column(db.DateTime()) def to_dict(self): last_used = None if self.last_used_on: last_used = "{}Z".format(self.last_used_on.isoformat()) return { "id": shortuuid.encode(self.id), "name": self.name, "account_id": shortuuid.encode(self.account_id), "access_token": self.access_token, "refresh_token": self.refresh_token, "client_id": self.client_id, "scope": self.scope, "token_type": self.token_type, "issued_at": self.issued_at, "expires_in": self.expires_in, "last_used": last_used, "revoked": self.revoked }
class UserPrivacy(db.Model): # type: ignore __bind_key__ = 'dashboard_service' id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4) account_id = db.Column(UUIDType(binary=False), db.ForeignKey('user_account.id'), nullable=False) last_changed = db.Column(db.DateTime(), server_default=func.now()) details = db.Column(JSONB())
class Client(db.Model, OAuth2ClientMixin): # type: ignore __bind_key__ = 'auth_service' id = db.Column(db.Integer, primary_key=True) account_id = db.Column(UUIDType(binary=False), db.ForeignKey('account.id', ondelete='CASCADE'), nullable=False) def to_dict(self): return { "id": self.id, "client_id": self.client_id, "client_secret": self.client_secret }
class LocalAccount(db.Model): # type: ignore __bind_key__ = 'auth_service' id = db.Column(UUIDType(binary=False), primary_key=True, unique=True, default=uuid.uuid4) account_id = db.Column(UUIDType(binary=False), db.ForeignKey('account.id', ondelete='CASCADE'), nullable=False) username = db.Column(db.String, nullable=False, unique=True, index=True) password = db.Column(PasswordType(schemes=['pbkdf2_sha512'], ), unique=False, nullable=False)
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 Execution(db.Model): # type: ignore __bind_key__ = 'experiment_service' __table_args__ = (db.UniqueConstraint('timestamp', 'experiment_id', name='index_per_experiment_uniq'), ) id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4) account_id = db.Column(UUIDType(binary=False), nullable=False, index=True) timestamp = db.Column( db.BigInteger, default=lambda: int(datetime.utcnow().timestamp() * 1000)) org_id = db.Column(UUIDType(binary=False), nullable=False, index=True) workspace_id = db.Column(UUIDType(binary=False), nullable=False, index=True) experiment_id = db.Column(UUIDType(binary=False), db.ForeignKey('experiment.id', ondelete='CASCADE'), nullable=False) status = db.Column(db.String) payload = db.Column(JSONB()) def to_dict(self, visibility: str = "status") -> Dict[str, Any]: result = { "id": shortuuid.encode(self.id), "timestamp": self.timestamp, "org": shortuuid.encode(self.org_id), "workspace": shortuuid.encode(self.workspace_id), "experiment": shortuuid.encode(self.experiment_id) } if visibility == "full": result["status"] = self.status result["result"] = self.payload elif visibility == "full": result["status"] = self.status return result
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]
class Workspace(db.Model): # type: ignore __bind_key__ = 'dashboard_service' __table_args__ = (db.UniqueConstraint('name', 'org_id', name='workspace_org_uniq'), ) id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4) name = db.Column(db.String(), nullable=False) name_lower = db.Column(db.String(), nullable=False) kind = db.Column(db.Enum(WorkspaceType), nullable=False, default=WorkspaceType.personal) org_id = db.Column(UUIDType(binary=False), db.ForeignKey('org.id'), nullable=False) settings = db.Column(JSONB(), nullable=False, default=DEFAULT_WORKSPACE_SETTINGS) def to_dict(self): return { "id": shortuuid.encode(self.id), "name": self.name, "type": self.kind.value, "org": { "id": shortuuid.encode(self.org_id), "name": self.org.name, "type": self.org.kind.value }, "settings": self.settings } def to_short_dict(self): return { "id": shortuuid.encode(self.id), "name": self.name, "type": self.kind.value, "settings": self.settings } @staticmethod def find_by_name(workspace_name: str) -> 'Workspace': """ Get a workspace by its name """ return Workspace.query.filter( Workspace.name_lower == workspace_name.lower()).first() @staticmethod def get_by_id(workspace_id: Union[str, uuid.UUID]) -> 'Workspace': """ Get a workspace by its identifier """ return Workspace.query.filter(Workspace.id == workspace_id).first() def is_collaborator(self, account_id: Union[str, uuid.UUID]) -> bool: """ Return `True` when the given account is a collaborator to this workspace """ return WorkpacesMembers.query.filter( WorkpacesMembers.workspace_id == self.id, WorkpacesMembers.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 workspace """ return WorkpacesMembers.query.filter( WorkpacesMembers.workspace_id == self.id, WorkpacesMembers.is_owner == True, WorkpacesMembers.account_id == account_id).first() is not None def has_single_owner(self) -> bool: """ Return `True` if only one owner exists for this workspace """ return WorkpacesMembers.query.filter( WorkpacesMembers.workspace_id == self.id, WorkpacesMembers.is_owner == True).count() == 1 def make_collaborator(self, account_id: Union[str, uuid.UUID]): """ Turn an user as a collaborator only of this workspace. """ membership = WorkpacesMembers.query.filter( WorkpacesMembers.workspace_id == self.id, WorkpacesMembers.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 workspace. """ membership = WorkpacesMembers.query.filter( WorkpacesMembers.workspace_id == self.id, WorkpacesMembers.account_id == account_id).first() if membership: membership.is_owner = True def add_collaborator(self, account_id: Union[str, uuid.UUID]) \ -> WorkpacesMembers: """ Add this user to the organization as a collaborator """ membership = WorkpacesMembers(workspace_id=self.id, account_id=account_id) db.session.add(membership) return membership def remove_collaborator(self, account_id: Union[str, uuid.UUID]): """ Remove this collaborator from the organization """ WorkpacesMembers.query.filter( WorkpacesMembers.workspace_id == self.id, WorkpacesMembers.account_id == account_id).delete()
class UserInfo(db.Model): # type: ignore __bind_key__ = 'dashboard_service' id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4) account_id = db.Column(UUIDType(binary=False), db.ForeignKey('user_account.id'), nullable=False) last_updated = db.Column(db.DateTime(), server_default=func.now(), onupdate=func.current_timestamp()) verified_email = db.Column(db.Boolean(), default=False) # values for search purpose mostly username = db.Column(db.String, index=True, nullable=True) fullname = db.Column(db.String, index=True, nullable=True) details = db.Column(EncryptedType(db.String, get_user_info_secret_key, AesEngine, 'pkcs5'), nullable=False) @staticmethod def get_for_account(account_id: Union[str, uuid.UUID]) -> 'UserInfo': """ Lookup user info for the given user account """ return UserInfo.query.filter(UserInfo.account_id == account_id).first() @property def profile(self) -> Dict[str, Any]: """ The user's profile """ return json.loads(self.details) @profile.setter def profile(self, p: Dict[str, Any]): """ Set the user's profile from the given payload The payload is serialized to JSON and stored in the `details` property """ self.details = json.dumps(p) def to_dict(self): return { "id": shortuuid.encode(self.id), "account": shortuuid.encode(self.account.id), "profile": self.profile } def to_public_dict(self): p = self.profile return { "id": shortuuid.encode(self.id), "username": p.get("preferred_username"), "name": p.get("name"), "picture": p.get("picture") }