class ProjectStatuses(db.Model): """ One-to-many table between projects and statuses. Contains all project status history. Primary key(s): - project_id - status Foreign key(s): - project_id """ # Table setup __tablename__ = "projectstatuses" # Foreign keys & relationships project_id = db.Column(db.Integer, db.ForeignKey("projects.id", ondelete="CASCADE"), primary_key=True) project = db.relationship("Project", back_populates="project_statuses") # --- # Additional columns status = db.Column(db.String(50), unique=False, nullable=False, primary_key=True) date_created = db.Column(db.DateTime(), nullable=False, primary_key=True) # Columns is_aborted = db.Column(db.Boolean, nullable=True, default=False, unique=False) deadline = db.Column(db.DateTime(), nullable=True)
class Version(db.Model): """ Data model for keeping track of all active and non active files. Used for invoicing. Primary key: - id Foreign key(s): - project_id - active_file """ # Table setup __tablename__ = "versions" __table_args__ = {"extend_existing": True} # Columns id = db.Column(db.Integer, primary_key=True, autoincrement=True) # Foreign keys & relationships project_id = db.Column(db.Integer, db.ForeignKey("projects.id", ondelete="RESTRICT"), nullable=False) project = db.relationship("Project", back_populates="file_versions") # --- active_file = db.Column(db.BigInteger, db.ForeignKey("files.id", ondelete="SET NULL"), nullable=True) file = db.relationship("File", back_populates="versions") # --- # Additional columns size_stored = db.Column(db.BigInteger, unique=False, nullable=False) time_uploaded = db.Column(db.DateTime(), unique=False, nullable=False, default=dds_web.utils.current_time()) time_deleted = db.Column(db.DateTime(), unique=False, nullable=True, default=None) time_invoiced = db.Column(db.DateTime(), unique=False, nullable=True, default=None) def __repr__(self): """Called by print, creates representation of object""" return f"<File Version {self.id}>"
class PasswordReset(db.Model): """Keep track of password resets.""" # Table setup __tablename__ = "password_resets" __table_args__ = {"extend_existing": True} # Primary Key id = db.Column(db.Integer, primary_key=True, autoincrement=True) user_id = db.Column(db.String(50), db.ForeignKey("users.username", ondelete="CASCADE")) user = db.relationship("User", back_populates="password_reset") email = db.Column(db.String(254), unique=True, nullable=False) issued = db.Column(db.DateTime(), unique=False, nullable=False) changed = db.Column(db.DateTime(), unique=False, nullable=True) valid = db.Column(db.Boolean, unique=False, nullable=False, default=True)
class Invite(db.Model): """ Invites for users not yet confirmed in DDS. Primary key: - id Foreign key(s): - unit_id """ # Table setup __tablename__ = "invites" __table_args__ = {"extend_existing": True} # Primary Key id = db.Column(db.Integer, primary_key=True, autoincrement=True) # Foreign keys & relationships unit_id = db.Column(db.Integer, db.ForeignKey("units.id", ondelete="CASCADE")) unit = db.relationship("Unit", back_populates="invites") project_invite_keys = db.relationship("ProjectInviteKeys", back_populates="invite", passive_deletes=True, cascade="all, delete") # --- # Additional columns email = db.Column(db.String(254), unique=True, nullable=False) role = db.Column(db.String(20), unique=False, nullable=False) nonce = db.Column(db.LargeBinary(12), default=None) public_key = db.Column(db.LargeBinary(300), default=None) private_key = db.Column(db.LargeBinary(300), default=None) created_at = db.Column(db.DateTime(), nullable=False, default=dds_web.utils.current_time()) @property def projects(self): """Return list of project items.""" return [proj.project for proj in self.project_associations] def __str__(self): """Called by str(), creates representation of object""" return f"Pending invite for {self.email}" def __repr__(self): """Called by print, creates representation of object""" return f"<Invite {self.email}>"
class MOTD(db.Model): """ Data model for keeping track of MOTD (message of the day). Primary key: - message """ # Table setup __tablename__ = "motd" __table_args__ = {"extend_existing": True} # Columns id = db.Column(db.Integer, primary_key=True, autoincrement=True) message = db.Column(db.Text, nullable=False, default=None) date_created = db.Column(db.DateTime(), nullable=False, default=None)
class File(db.Model): """ Data model for files. Primary key: - id Foreign key(s): - project_id """ # Table setup __tablename__ = "files" __table_args__ = {"extend_existing": True} # Columns id = db.Column(db.BigInteger, primary_key=True, autoincrement=True) # Foreign keys & relationships project_id = db.Column(db.Integer, db.ForeignKey("projects.id", ondelete="RESTRICT"), index=True, nullable=False) project = db.relationship("Project", back_populates="files") # --- # Additional columns name = db.Column(db.Text, unique=False, nullable=False) name_in_bucket = db.Column(db.Text, unique=False, nullable=False) subpath = db.Column(db.Text, unique=False, nullable=False) size_original = db.Column(db.BigInteger, unique=False, nullable=False) size_stored = db.Column(db.BigInteger, unique=False, nullable=False) compressed = db.Column(db.Boolean, nullable=False) public_key = db.Column(db.String(64), unique=False, nullable=False) salt = db.Column(db.String(32), unique=False, nullable=False) checksum = db.Column(db.String(64), unique=False, nullable=False) time_latest_download = db.Column(db.DateTime(), unique=False, nullable=True) # Additional relationships versions = db.relationship("Version", back_populates="file") def __repr__(self): """Called by print, creates representation of object""" return f"<File {pathlib.Path(self.name).name}>"
class DeletionRequest(db.Model): """Table to collect self-deletion requests by users""" # Table setup __tablename__ = "deletions" __table_args__ = {"extend_existing": True} # Primary Key id = db.Column(db.Integer, primary_key=True, autoincrement=True) requester_id = db.Column( db.String(50), db.ForeignKey("users.username", ondelete="CASCADE")) requester = db.relationship("User", back_populates="deletion_request") email = db.Column(db.String(254), unique=True, nullable=False) issued = db.Column(db.DateTime(), unique=False, nullable=False) def __repr__(self): """Called by print, creates representation of object""" return f"<DeletionRequest {self.email}>"
class Project(db.Model): """ Data model for projects. Primary key(s): - id Foreign key(s): - unit_id - created_by """ # Table setup __tablename__ = "projects" __table_args__ = {"extend_existing": True} # Columns id = db.Column(db.Integer, primary_key=True, autoincrement=True) public_id = db.Column(db.String(255), unique=True, nullable=True) title = db.Column(db.Text, unique=False, nullable=True) date_created = db.Column( db.DateTime(), nullable=True, default=dds_web.utils.current_time(), ) date_updated = db.Column(db.DateTime(), nullable=True) description = db.Column(db.Text) pi = db.Column(db.String(255), unique=False, nullable=True) bucket = db.Column(db.String(255), unique=True, nullable=False) public_key = db.Column(db.LargeBinary(100), nullable=True) non_sensitive = db.Column(db.Boolean, unique=False, default=False, nullable=False) released = db.Column(db.DateTime(), nullable=True) is_active = db.Column(db.Boolean, unique=False, nullable=False, default=True, index=True) # Foreign keys & relationships unit_id = db.Column(db.Integer, db.ForeignKey("units.id", ondelete="RESTRICT"), nullable=True) responsible_unit = db.relationship("Unit", back_populates="projects") # --- created_by = db.Column( db.String(50), db.ForeignKey("users.username", ondelete="SET NULL")) creator = db.relationship("User", backref="created_projects", foreign_keys=[created_by]) last_updated_by = db.Column( db.String(50), db.ForeignKey("users.username", ondelete="SET NULL")) updator = db.relationship("User", backref="updated_projects", foreign_keys=[last_updated_by]) # --- # Additional relationships files = db.relationship("File", back_populates="project") file_versions = db.relationship("Version", back_populates="project") project_statuses = db.relationship("ProjectStatuses", back_populates="project", passive_deletes=True, cascade="all, delete") researchusers = db.relationship("ProjectUsers", back_populates="project", passive_deletes=True, cascade="all, delete") project_user_keys = db.relationship("ProjectUserKeys", back_populates="project", passive_deletes=True) project_invite_keys = db.relationship("ProjectInviteKeys", back_populates="project", passive_deletes=True) @property def current_status(self): """Return the current status of the project""" return max(self.project_statuses, key=lambda x: x.date_created).status @property def has_been_available(self): """Return True if the project has ever been in the status Available""" result = False if len([x for x in self.project_statuses if "Available" in x.status ]) > 0: result = True return result @property def times_expired(self): return len([x for x in self.project_statuses if "Expired" in x.status]) @property def current_deadline(self): """Return deadline for statuses that have a deadline""" deadline = None if self.current_status in ["Available", "Expired"]: deadline = max(self.project_statuses, key=lambda x: x.date_created).deadline elif self.current_status in ["In Progress"]: if self.has_been_available: list_available = list( filter(lambda x: x.status == "Available", self.project_statuses)) latest_available = max(list_available, key=lambda x: x.date_created) deadline = latest_available.deadline return deadline @property def safespring_project(self): """Get the safespring project name from responsible unit.""" return self.responsible_unit.safespring @property def size(self): """Calculate size of project.""" return sum([f.size_stored for f in self.files]) @property def num_files(self): """Get number of files in project.""" return len(self.files) def __str__(self): """Called by str(), creates representation of object""" return f"Project {self.public_id}" def __repr__(self): """Called by print, creates representation of object""" return f"<Project {self.public_id}>"