diff --git a/reana_db/alembic/versions/20201009_1612_ad93dae04483_interactive_sessions.py b/reana_db/alembic/versions/20201009_1612_ad93dae04483_interactive_sessions.py new file mode 100644 index 0000000..127fe6f --- /dev/null +++ b/reana_db/alembic/versions/20201009_1612_ad93dae04483_interactive_sessions.py @@ -0,0 +1,103 @@ +"""Interactive sessions. + +Revision ID: ad93dae04483 +Revises: c912d4f1e1cc +Create Date: 2020-10-09 16:12:00.090837 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utils + +# revision identifiers, used by Alembic. +revision = "ad93dae04483" +down_revision = "c912d4f1e1cc" +branch_labels = None +depends_on = None + + +def upgrade(): + """Upgrade to ad93dae04483 revision.""" + op.create_table( + "interactive_session", + sa.Column("created", sa.DateTime(), nullable=False), + sa.Column("updated", sa.DateTime(), nullable=False), + sa.Column("id_", sqlalchemy_utils.types.uuid.UUIDType(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=True), + sa.Column("path", sa.Text(), nullable=True), + sa.Column( + "status", + sa.Enum( + "created", + "running", + "finished", + "failed", + "deleted", + "stopped", + "queued", + name="runstatus", + ), + nullable=False, + ), + sa.Column("owner_id", sqlalchemy_utils.types.uuid.UUIDType(), nullable=True), + sa.Column( + "type_", sa.Enum("jupyter", name="interactivesessiontype"), nullable=False + ), + sa.ForeignKeyConstraint(["owner_id"], ["__reana.user_.id_"],), + sa.PrimaryKeyConstraint("id_"), + sa.UniqueConstraint("id_"), + sa.UniqueConstraint("name", "path", name="_interactive_session_uc"), + schema="__reana", + ) + op.create_table( + "interactive_session_resource", + sa.Column("created", sa.DateTime(), nullable=False), + sa.Column("updated", sa.DateTime(), nullable=False), + sa.Column("session_id", sqlalchemy_utils.types.uuid.UUIDType(), nullable=False), + sa.Column( + "resource_id", sqlalchemy_utils.types.uuid.UUIDType(), nullable=False + ), + sa.Column("quantity_used", sa.BigInteger(), nullable=True), + sa.ForeignKeyConstraint(["resource_id"], ["__reana.resource.id_"],), + sa.ForeignKeyConstraint(["session_id"], ["__reana.interactive_session.id_"],), + sa.PrimaryKeyConstraint("session_id", "resource_id"), + schema="__reana", + ) + op.create_table( + "workflow_session", + sa.Column("workflow_id", sqlalchemy_utils.types.uuid.UUIDType(), nullable=True), + sa.Column("session_id", sqlalchemy_utils.types.uuid.UUIDType(), nullable=False), + sa.ForeignKeyConstraint(["session_id"], ["__reana.interactive_session.id_"],), + sa.ForeignKeyConstraint(["workflow_id"], ["__reana.workflow.id_"],), + sa.PrimaryKeyConstraint("session_id"), + schema="__reana", + ) + op.drop_column("workflow", "interactive_session_name", schema="__reana") + op.drop_column("workflow", "interactive_session_type", schema="__reana") + op.drop_column("workflow", "interactive_session", schema="__reana") + + +def downgrade(): + """Downgrade to previous revision (none in this case).""" + op.add_column( + "workflow", + sa.Column("interactive_session", sa.TEXT(), autoincrement=False, nullable=True), + schema="__reana", + ) + op.add_column( + "workflow", + sa.Column( + "interactive_session_type", sa.TEXT(), autoincrement=False, nullable=True + ), + schema="__reana", + ) + op.add_column( + "workflow", + sa.Column( + "interactive_session_name", sa.TEXT(), autoincrement=False, nullable=True + ), + schema="__reana", + ) + op.drop_table("workflow_session", schema="__reana") + op.drop_table("interactive_session_resource", schema="__reana") + op.drop_table("interactive_session", schema="__reana") diff --git a/reana_db/models.py b/reana_db/models.py index 89e0938..1c2a39d 100644 --- a/reana_db/models.py +++ b/reana_db/models.py @@ -290,23 +290,20 @@ class JobStatus(enum.Enum): queued = 5 -class UserWorkflowSession(Base, Timestamp): - """User Workflow Session table.""" +class WorkflowSession(Base): + """Workflow Session table.""" - __tablename__ = "user_workflow_session" + __tablename__ = "workflow_session" __table_args__ = {"schema": "__reana"} - user_id = Column(UUIDType, ForeignKey("__reana.user_.id_"), nullable=False) workflow_id = Column(UUIDType, ForeignKey("__reana.workflow.id_"), nullable=True) session_id = Column( UUIDType, ForeignKey("__reana.interactive_session.id_"), primary_key=True ) def __repr__(self): - """User Workflow Session string representation.""" - return "".format( - self.user_id, self.session_id, self.workflow_id - ) + """Workflow Session string representation.""" + return "".format(self.session_id, self.workflow_id) class InteractiveSessionType(enum.Enum): @@ -322,9 +319,7 @@ class InteractiveSession(Base, Timestamp): id_ = Column(UUIDType, primary_key=True, unique=True, default=generate_uuid) name = Column(String(255)) path = Column(Text) # path to access the interactive session - status = Column( - Enum(RunStatus), nullable=False, default=RunStatus.created - ) + status = Column(Enum(RunStatus), nullable=False, default=RunStatus.created) owner_id = Column(UUIDType, ForeignKey("__reana.user_.id_")) type_ = Column( Enum(InteractiveSessionType), @@ -374,6 +369,14 @@ class Workflow(Base, Timestamp): git_provider = Column(String(255)) owner = relationship("User", backref="workflow") + sessions = relationship( + "InteractiveSession", + secondary="__reana.workflow_session", + lazy="dynamic", + backref="workflow", + cascade="all, delete", + ) + __table_args__ = ( UniqueConstraint( "name", "owner_id", "run_number", name="_user_workflow_run_uc" @@ -714,6 +717,25 @@ def __repr__(self): return "".format(self.workflow_id, self.resource_id) +class InteractiveSessionResource(Base, Timestamp): + """Interactive Session Resource table.""" + + __tablename__ = "interactive_session_resource" + __table_args__ = {"schema": "__reana"} + + session_id = Column( + UUIDType, ForeignKey("__reana.interactive_session.id_"), primary_key=True + ) + resource_id = Column(UUIDType, ForeignKey("__reana.resource.id_"), primary_key=True) + quantity_used = Column(BigInteger()) + + def __repr__(self): + """Interactive Session Resource string representation.""" + return "".format( + self.session_id, self.resource_id + ) + + class QuotaHealth(enum.Enum): """Enumeration of quota health statuses."""