Beispiel #1
0
class SnapshotModel(BaseModel):
    __tablename__ = 'snapshot'
    #:Primary key of the Model
    id = db.Column(db.Integer, primary_key=True)
    #:Metadata for IAP
    measurement_tool = db.Column(db.String)
    #:The ID of the phenobox which was used to create this snapshot
    phenobox_id = db.Column(UUID(as_uuid=True), nullable=False)
    #:Metadata for IAP
    camera_position = db.Column(
        ENUM('vis.side', 'vis.top', 'fluo.top', 'fluo.side', 'nir.top', 'nir.side', 'ir.top', 'ir.side',
             name='camera_position_enum'), nullable=False,
        server_default='vis.side')
    #:Flag used to exclude this snapshot from analysis
    excluded = db.Column(db.Boolean, server_default='f', default=False, nullable=False)
    #:Foreign key to the corresponding Timestamp
    timestamp_id = db.Column(db.Integer, db.ForeignKey('timestamp.id'), nullable=False)
    #:SQLAlchemy relationship to the corresponding Timestamp
    timestamp = db.relationship("TimestampModel", back_populates="snapshots", single_parent=True)
    #:Foreign key to the corresponding Timestamp
    plant_id = db.Column(db.Integer, db.ForeignKey('plant.id'), nullable=False)
    #:SQLAlchemy relationship to the corresponding Plant
    plant = db.relationship("PlantModel", back_populates="snapshots", single_parent=True)
    #:SQLAlchemy relationship to all images belonging to this snapshot
    images = db.relationship("ImageModel", back_populates="snapshot", cascade="all, delete-orphan", lazy='dynamic')
    #:SQLAlchemy relationship to all analyses performed on this snapshot
    postprocesses = db.relationship("PostprocessModel", secondary='postprocess_snapshot', back_populates='snapshots')

    db.UniqueConstraint(plant_id, timestamp_id, name=u'uq_snapshot_plant_id_timestamp_id')

    def purge(self):
        # Only allow to delete a snapshot from an uncompleted timestamp
        if not self.timestamp.completed:
            shared_folder_map = current_app.config['SHARED_FOLDER_MAP']
            raw_image_path = None
            for image in self.images:
                if image.type == 'raw':
                    if raw_image_path is None:
                        raw_image_path = get_local_path_from_smb(image.path, shared_folder_map)
                    os.remove(os.path.join(raw_image_path, image.filename))
                db.session.delete(image)
            db.session.delete(self)
            db.session.commit()
            return True
        else:
            return False  # TODO throw exceptions instead of returning true or false

    def __init__(self, plant_id, timestamp_id, camera_position, measurement_tool, phenobox_id):
        self.plant_id = plant_id
        self.timestamp_id = timestamp_id
        self.camera_position = camera_position
        self.measurement_tool = measurement_tool
        self.phenobox_id = phenobox_id

    def __repr__(self):
        return '<Snapshot %d of plant %r>' % (self.id, self.plant.name)
class AnalysisModel(BaseModel):
    __tablename__ = 'analysis'
    #:Primary key of the Model
    id = db.Column(db.Integer, primary_key=True)
    #:The IAP ID of the Analyis results in IAP
    iap_id = db.Column(db.String, unique=True)
    #:The path, as SMB URL, where the exported IAP results are stored
    export_path = db.Column(db.String, nullable=True)
    #:The id of the IAP pipeline with which this analysis has been processed
    pipeline_id = db.Column(db.String, nullable=False)
    #:Timestamp to indicate the start time of the analysis
    started_at = db.Column(db.DateTime, nullable=True)
    #:Timestamp to indicate the end time of the analysis
    finished_at = db.Column(db.DateTime, nullable=True)
    #:Foreign key to the corresponding Timestamp
    timestamp_id = db.Column(db.Integer, db.ForeignKey('timestamp.id'), nullable=False)
    #:SQLAlchemy relationship to the corresponding Timestamp
    timestamp = db.relationship("TimestampModel", back_populates="analyses", single_parent=True)
    #:SQLAlchemy relationship to all Postprocessings which have been applied to this analysis
    postprocessings = db.relationship("PostprocessModel", back_populates="analysis", cascade="all, delete-orphan")
    #:SQLAlchemy relationship to all Snapshots processed by this analysis
    db.UniqueConstraint(timestamp_id, pipeline_id, name=u'uq_analysis_timestamp_id_pipeline_id')

    @staticmethod
    def get_or_create(timestamp_id, pipeline_id, session=None):
        if session is None:
            session = db.session
        try:
            return session.query(AnalysisModel).filter_by(timestamp_id=timestamp_id,
                                                          pipeline_id=pipeline_id).one(), False
        except NoResultFound:
            entry = AnalysisModel(timestamp_id, pipeline_id)
            try:
                session.add(entry)
                session.flush()
                return entry, True
            except IntegrityError:
                session.rollback()
                return session.query(AnalysisModel).filter_by(timestamp_id=timestamp_id,
                                                              pipeline_id=pipeline_id).one(), False

    def purge(self):
        shared_folder_map = current_app.config['SHARED_FOLDER_MAP']
        for postprocess in self.postprocessings:
            postprocess.purge()
        local_path = get_local_path_from_smb(self.report_path, shared_folder_map)
        shutil.rmtree(local_path)
        for image in self.timestamp.snapshots.images.where(ImageModel.type == 'segmented'):
            db.session.delete(image)

    def __init__(self, timestamp_id, pipeline_id):
        self.timestamp_id = timestamp_id
        self.pipeline_id = pipeline_id

    def __repr__(self):
        return '<Analysis %d with Pipeline %r of timestamp %d>' % (self.id, self.pipeline_id, self.timestamp_id)
class ImageModel(BaseModel):
    __tablename__ = 'image'
    #:Primary key of the Model
    id = db.Column(db.Integer, primary_key=True)
    #:The path, as SMB URL, to the image file
    path = db.Column(db.String, nullable=False)
    #:The filename of the image
    filename = db.Column(db.String, nullable=False)
    #:The type of the image. Indicates whether this image was processed or not
    type = db.Column(ENUM('raw', 'segmented', name='image_type_enum'),
                     default="raw",
                     server_default='raw',
                     nullable=False)
    #:The angle at which the image was taken
    angle = db.Column(db.Integer, nullable=False)
    #:Foreign key to the corresponding Snapshot
    snapshot_id = db.Column(db.Integer,
                            db.ForeignKey('snapshot.id'),
                            nullable=False)
    #:SQLAlchemy relationship to the corresponding Snapshot
    snapshot = db.relationship("SnapshotModel",
                               back_populates="images",
                               single_parent=True)

    db.UniqueConstraint(snapshot_id,
                        type,
                        angle,
                        name=u'uq_image_snapshot_id_type_angle')
    db.UniqueConstraint(path, filename, name=u'uq_image_path_filename')

    def __init__(self, snapshot_id, path, filename, angle, image_type):
        self.snapshot_id = snapshot_id
        self.path = path
        self.filename = filename
        self.type = image_type
        self.angle = angle

    def __repr__(self):
        return '<Image %d of plant %r>' % (self.id, self.filename)
Beispiel #4
0
class PlantModel(BaseModel):
    __tablename__ = 'plant'
    #:Primary key of the Model
    id = db.Column(db.Integer, primary_key=True)
    #:The detailed name of the plant
    name = db.Column(db.String(80))
    #:The index of the plant in the group
    index = db.Column(db.Integer)
    #:Foreign key to the corresponding Sample Group
    sample_group_id = db.Column(db.Integer, db.ForeignKey('sample_group.id'))
    #:SQLAlchemy relationship to the corresponding Sample Group
    sample_group = db.relationship("SampleGroupModel",
                                   back_populates="plants",
                                   single_parent=True)
    #:SQLAlchemy relationship to all Snapshots that belong to this plant
    snapshots = db.relationship("SnapshotModel",
                                back_populates="plant",
                                cascade="all, delete-orphan")

    db.UniqueConstraint(index,
                        sample_group_id,
                        name=u'uq_plant_index_sample_group_id')

    @hybrid_property
    def full_name(self):
        if self.name != '':
            return '{}_{}_{}'.format(self.sample_group.name, self.index,
                                     self.name)
        else:
            return '{}_{}'.format(self.sample_group.name, self.index)

    def purge(self):
        for snapshot in self.snapshots:
            snapshot.purge()

    def __init__(self, index, name, sample_group_id):
        self.index = index
        self.name = name
        self.sample_group_id = sample_group_id

    def __repr__(self):
        return '<Plant %r>' % self.name
Beispiel #5
0
class SampleGroupModel(BaseModel):
    __tablename__ = 'sample_group'
    #:Primary key of the Model
    id = db.Column(db.Integer, primary_key=True)
    #:The name of the sample group
    name = db.Column(db.String(80), nullable=False)
    #:A detailed description of the group
    description = db.Column(db.Text)
    #:Metadata for IAP
    species = db.Column(db.String)
    #:Metadata for IAP
    genotype = db.Column(db.String)
    #:Metadata for IAP
    variety = db.Column(db.String)
    #:Metadata for IAP
    growth_conditions = db.Column(db.String)
    #:Metadata for IAP
    treatment = db.Column(
        db.String, nullable=True
    )  # TODO change to False after all values have been updated
    #:Indicates whether this group is a control group or not
    is_control = db.Column(db.Boolean,
                           nullable=False,
                           default=False,
                           server_default='f')
    #:Foreign key to the corresponding Experiment
    experiment_id = db.Column(db.Integer,
                              db.ForeignKey('experiment.id'),
                              nullable=False)
    #:SQLAlchemy relationship to the corresponding Experiment
    experiment = db.relationship("ExperimentModel",
                                 back_populates="sample_groups",
                                 single_parent=True)
    #:SQLAlchemy relationship to all plants belonging to this sample group
    plants = db.relationship("PlantModel",
                             back_populates="sample_group",
                             cascade="all, delete-orphan")

    db.UniqueConstraint(name, experiment_id)
    db.UniqueConstraint(treatment, experiment_id)

    def __init__(self,
                 name,
                 treatment,
                 description,
                 species,
                 genotype,
                 variety,
                 growth_conditions,
                 experiment_id,
                 is_control=False):
        if name and name.strip() == "":
            name = None
        if treatment and treatment.strip() == "":
            treatment = None

        self.name = name
        self.treatment = treatment
        self.description = description
        self.experiment_id = experiment_id
        self.species = species
        self.genotype = genotype
        self.variety = variety
        self.growth_conditions = growth_conditions
        self.is_control = is_control

    @classmethod
    def fromdict(self, group_data, experiment_id):
        for key, value in group_data.items():
            if type(value) is unicode and value.strip() == "":
                del group_data[key]
        return self(group_data.get('name'),
                    group_data.get('treatment'),
                    group_data.get('description'),
                    group_data.get('species'),
                    group_data.get('genotype'),
                    group_data.get('variety'),
                    group_data.get('growth_conditions'),
                    experiment_id=experiment_id,
                    is_control=group_data.get('is_control'))

    def __repr__(self):
        return '<SampleGroup %r>' % self.name
Beispiel #6
0
class PostprocessModel(BaseModel):
    __tablename__ = 'postprocess'
    #:Primary key of the Model
    id = db.Column(db.Integer, primary_key=True)
    snapshot_hash = db.Column(db.BIGINT, nullable=False)
    #:Path, as SMB URL, to the results of this postprocess
    result_path = db.Column(db.String, nullable=True)
    #:The ID of the postprocessing stack that was applied
    postprocessing_stack_id = db.Column(db.String, nullable=False)
    #:The ID of the sample group used as control group
    control_group_id = db.Column(db.Integer,
                                 db.ForeignKey("sample_group.id"),
                                 nullable=False)
    #:Timestamp to indicate the start time of the postprocessing
    started_at = db.Column(db.DateTime, nullable=True)
    #:Timestamp to indicate the end time of the postprocessing
    finished_at = db.Column(db.DateTime, nullable=True)
    #:A note from the user to make it easier to identify a postprocess
    note = db.Column(db.Text, nullable=True)
    #:Foreign key to the corresponding analysis
    analysis_id = db.Column(db.Integer,
                            db.ForeignKey('analysis.id'),
                            nullable=False)
    #:SQLAlchemy relationship to the corresponding analysis
    analysis = db.relationship("AnalysisModel",
                               back_populates="postprocessings",
                               single_parent=True)
    #:SQLAlchemy relationship to all Snapshots processed by this postprocess
    snapshots = db.relationship("SnapshotModel",
                                secondary='postprocess_snapshot',
                                back_populates='postprocesses')
    #:SQLAlchemy relationship to the sample group used as control group
    control_group = db.relationship("SampleGroupModel")

    db.UniqueConstraint(
        snapshot_hash,
        postprocessing_stack_id,
        analysis_id,
        control_group_id,
        name=
        u'uq_postprocess_snapshot_hash_postprocessing_stack_id_analysis_id_control_group_id'
    )

    def purge(self):
        shared_folder_map = current_app.config['SHARED_FOLDER_MAP']
        local_path = get_local_path_from_smb(self.result_path,
                                             shared_folder_map)
        shutil.rmtree(local_path)

    @staticmethod
    def calculate_snapshot_hash(snapshots):
        return hash(frozenset([snapshot.id for snapshot in snapshots]))

    @staticmethod
    def get_or_create(analysis_id,
                      postprocessing_stack_id,
                      control_group_id,
                      snapshots,
                      note=None,
                      session=None):
        snap_hash = PostprocessModel.calculate_snapshot_hash(snapshots)
        if session is None:
            session = db.session
        try:
            return session.query(PostprocessModel).filter_by(
                analysis_id=analysis_id,
                postprocessing_stack_id=postprocessing_stack_id,
                snapshot_hash=snap_hash).one(), False
        except NoResultFound:
            entry = PostprocessModel(analysis_id, postprocessing_stack_id,
                                     control_group_id, snapshots, note,
                                     snap_hash)
            try:
                session.add(entry)
                session.flush()
                return entry, True
            except IntegrityError:
                session.rollback()
                return session.query(PostprocessModel).filter_by(
                    analyis_id=analysis_id,
                    postprocessing_stack_id=postprocessing_stack_id,
                    snapshot_hash=snap_hash).one(), False

    def __init__(self,
                 analysis_id,
                 postprocessing_stack_id,
                 control_group_id,
                 snapshots,
                 note=None,
                 snapshot_hash=None):
        self.analysis_id = analysis_id
        self.postprocessing_stack_id = postprocessing_stack_id
        self.control_group_id = control_group_id
        self.snapshots = snapshots
        self.note = note
        # TODO always calculate snapshot hash to prevent incorrect hashes?
        if snapshot_hash is not None:
            self.snapshot_hash = snapshot_hash
        else:
            self.snapshot_hash = self.calculate_snapshot_hash(snapshots)

    def __repr__(self):
        return '<Postprocessing %d with stack %r of analysis %d>' % (
            self.id, self.postprocessing_stack_id, self.analysis_id)