Skip to content

models

persistence.models

BatchSimulationModel

Bases: SimulationModel

Batch simulation model

Source code in yaptide/persistence/models.py
143
144
145
146
147
148
149
150
151
152
153
class BatchSimulationModel(SimulationModel):
    """Batch simulation model"""

    __tablename__ = 'BatchSimulation'
    id: Column[int] = db.Column(db.Integer, db.ForeignKey('Simulation.id', ondelete="CASCADE"), primary_key=True)
    cluster_id: Column[int] = db.Column(db.Integer, db.ForeignKey('Cluster.id'), nullable=False, doc="Cluster ID")
    job_dir: Column[str] = db.Column(db.String, nullable=True, doc="Simulation folder name")
    array_id: Column[int] = db.Column(db.Integer, nullable=True, doc="Batch array jon ID")
    collect_id: Column[int] = db.Column(db.Integer, nullable=True, doc="Batch collect job ID")

    __mapper_args__ = {"polymorphic_identity": PlatformType.BATCH.value, "polymorphic_load": "inline"}

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": value,
    "polymorphic_load": "inline",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'BatchSimulation'

array_id class-attribute instance-attribute

array_id = Column(
    Integer, nullable=True, doc="Batch array jon ID"
)

cluster_id class-attribute instance-attribute

cluster_id = Column(
    Integer,
    ForeignKey("Cluster.id"),
    nullable=False,
    doc="Cluster ID",
)

collect_id class-attribute instance-attribute

collect_id = Column(
    Integer, nullable=True, doc="Batch collect job ID"
)

id class-attribute instance-attribute

id = Column(
    Integer,
    ForeignKey("Simulation.id", ondelete="CASCADE"),
    primary_key=True,
)

job_dir class-attribute instance-attribute

job_dir = Column(
    String, nullable=True, doc="Simulation folder name"
)

BatchTaskModel

Bases: TaskModel

Batch task model

Source code in yaptide/persistence/models.py
258
259
260
261
262
263
264
class BatchTaskModel(TaskModel):
    """Batch task model"""

    __tablename__ = 'BatchTask'
    id: Column[int] = db.Column(db.Integer, db.ForeignKey('Task.id', ondelete="CASCADE"), primary_key=True)

    __mapper_args__ = {"polymorphic_identity": PlatformType.BATCH.value, "polymorphic_load": "inline"}

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": value,
    "polymorphic_load": "inline",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'BatchTask'

id class-attribute instance-attribute

id = Column(
    Integer,
    ForeignKey("Task.id", ondelete="CASCADE"),
    primary_key=True,
)

CelerySimulationModel

Bases: SimulationModel

Celery simulation model

Source code in yaptide/persistence/models.py
133
134
135
136
137
138
139
140
class CelerySimulationModel(SimulationModel):
    """Celery simulation model"""

    __tablename__ = 'CelerySimulation'
    id: Column[int] = db.Column(db.Integer, db.ForeignKey('Simulation.id', ondelete="CASCADE"), primary_key=True)
    merge_id: Column[str] = db.Column(db.String, nullable=True, doc="Celery collect job ID")

    __mapper_args__ = {"polymorphic_identity": PlatformType.DIRECT.value, "polymorphic_load": "inline"}

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": value,
    "polymorphic_load": "inline",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'CelerySimulation'

id class-attribute instance-attribute

id = Column(
    Integer,
    ForeignKey("Simulation.id", ondelete="CASCADE"),
    primary_key=True,
)

merge_id class-attribute instance-attribute

merge_id = Column(
    String, nullable=True, doc="Celery collect job ID"
)

CeleryTaskModel

Bases: TaskModel

Celery task model

Source code in yaptide/persistence/models.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
class CeleryTaskModel(TaskModel):
    """Celery task model"""

    __tablename__ = 'CeleryTask'
    id: Column[int] = db.Column(db.Integer, db.ForeignKey('Task.id', ondelete="CASCADE"), primary_key=True)
    celery_id: Column[str] = db.Column(db.String, nullable=False, default="", doc="Celery task ID")

    def update_state(self, update_dict: dict):
        """Update method for CeleryTaskModel"""
        if "celery_id" in update_dict and self.celery_id != update_dict["celery_id"]:
            self.celery_id = update_dict["celery_id"]
        return super().update_state(update_dict)

    __mapper_args__ = {"polymorphic_identity": PlatformType.DIRECT.value, "polymorphic_load": "inline"}

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": value,
    "polymorphic_load": "inline",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'CeleryTask'

celery_id class-attribute instance-attribute

celery_id = Column(
    String, nullable=False, default="", doc="Celery task ID"
)

id class-attribute instance-attribute

id = Column(
    Integer,
    ForeignKey("Task.id", ondelete="CASCADE"),
    primary_key=True,
)

update_state

update_state(update_dict)

Update method for CeleryTaskModel

Source code in yaptide/persistence/models.py
249
250
251
252
253
def update_state(self, update_dict: dict):
    """Update method for CeleryTaskModel"""
    if "celery_id" in update_dict and self.celery_id != update_dict["celery_id"]:
        self.celery_id = update_dict["celery_id"]
    return super().update_state(update_dict)

ClusterModel

Bases: Model

Cluster info for specific user

Source code in yaptide/persistence/models.py
60
61
62
63
64
65
66
class ClusterModel(db.Model):
    """Cluster info for specific user"""

    __tablename__ = 'Cluster'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    cluster_name: Column[str] = db.Column(db.String, nullable=False)
    simulations = relationship("BatchSimulationModel")

__tablename__ class-attribute instance-attribute

__tablename__ = 'Cluster'

cluster_name class-attribute instance-attribute

cluster_name = Column(String, nullable=False)

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

simulations class-attribute instance-attribute

simulations = relationship('BatchSimulationModel')

EstimatorModel

Bases: Model

Simulation single estimator model

Source code in yaptide/persistence/models.py
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
class EstimatorModel(db.Model):
    """Simulation single estimator model"""

    __tablename__ = 'Estimator'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    simulation_id: Column[int] = db.Column(db.Integer, db.ForeignKey('Simulation.id'), nullable=False)
    name: Column[str] = db.Column(db.String, nullable=False, doc="Estimator name")
    compressed_data: Column[bytes] = db.Column(db.LargeBinary, doc="Estimator metadata")

    @property
    def data(self):
        return decompress(self.compressed_data)

    @data.setter
    def data(self, value):
        if value is not None:
            self.compressed_data = compress(value)

__tablename__ class-attribute instance-attribute

__tablename__ = 'Estimator'

compressed_data class-attribute instance-attribute

compressed_data = Column(
    LargeBinary, doc="Estimator metadata"
)

data property writable

data

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

name class-attribute instance-attribute

name = Column(String, nullable=False, doc='Estimator name')

simulation_id class-attribute instance-attribute

simulation_id = Column(
    Integer, ForeignKey("Simulation.id"), nullable=False
)

InputModel

Bases: Model

Simulation inputs model

Source code in yaptide/persistence/models.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
class InputModel(db.Model):
    """Simulation inputs model"""

    __tablename__ = 'Input'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    simulation_id: Column[int] = db.Column(db.Integer, db.ForeignKey('Simulation.id'))
    compressed_data: Column[bytes] = db.Column(db.LargeBinary)

    @property
    def data(self):
        return decompress(self.compressed_data)

    @data.setter
    def data(self, value):
        if value is not None:
            self.compressed_data = compress(value)

__tablename__ class-attribute instance-attribute

__tablename__ = 'Input'

compressed_data class-attribute instance-attribute

compressed_data = Column(LargeBinary)

data property writable

data

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

simulation_id class-attribute instance-attribute

simulation_id = Column(Integer, ForeignKey("Simulation.id"))

KeycloakUserModel

Bases: UserModel, Model

PLGrid user model

Source code in yaptide/persistence/models.py
49
50
51
52
53
54
55
56
57
class KeycloakUserModel(UserModel, db.Model):
    """PLGrid user model"""

    __tablename__ = 'KeycloakUser'
    id: Column[int] = db.Column(db.Integer, db.ForeignKey('User.id', ondelete="CASCADE"), primary_key=True)
    cert: Column[str] = db.Column(db.String, nullable=True)
    private_key: Column[str] = db.Column(db.String, nullable=True)

    __mapper_args__ = {"polymorphic_identity": "KeycloakUser", "polymorphic_load": "inline"}

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": "KeycloakUser",
    "polymorphic_load": "inline",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'KeycloakUser'

cert class-attribute instance-attribute

cert = Column(String, nullable=True)

id class-attribute instance-attribute

id = Column(
    Integer,
    ForeignKey("User.id", ondelete="CASCADE"),
    primary_key=True,
)

private_key class-attribute instance-attribute

private_key = Column(String, nullable=True)

LogfilesModel

Bases: Model

Simulation logfiles model

Source code in yaptide/persistence/models.py
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
class LogfilesModel(db.Model):
    """Simulation logfiles model"""

    __tablename__ = 'Logfiles'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    simulation_id: Column[int] = db.Column(db.Integer, db.ForeignKey('Simulation.id'), nullable=False)
    compressed_data: Column[bytes] = db.Column(db.LargeBinary, doc="Json object containing logfiles")

    @property
    def data(self):
        return decompress(self.compressed_data)

    @data.setter
    def data(self, value):
        if value is not None:
            self.compressed_data = compress(value)

__tablename__ class-attribute instance-attribute

__tablename__ = 'Logfiles'

compressed_data class-attribute instance-attribute

compressed_data = Column(
    LargeBinary, doc="Json object containing logfiles"
)

data property writable

data

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

simulation_id class-attribute instance-attribute

simulation_id = Column(
    Integer, ForeignKey("Simulation.id"), nullable=False
)

PageModel

Bases: Model

Estimator single page model

Source code in yaptide/persistence/models.py
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
class PageModel(db.Model):
    """Estimator single page model"""

    __tablename__ = 'Page'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    estimator_id: Column[int] = db.Column(db.Integer, db.ForeignKey('Estimator.id'), nullable=False)
    page_number: Column[int] = db.Column(db.Integer, nullable=False, doc="Page number")
    compressed_data: Column[bytes] = db.Column(db.LargeBinary, doc="Page json object - data, axes and metadata")

    @property
    def data(self):
        return decompress(self.compressed_data)

    @data.setter
    def data(self, value):
        if value is not None:
            self.compressed_data = compress(value)

__tablename__ class-attribute instance-attribute

__tablename__ = 'Page'

compressed_data class-attribute instance-attribute

compressed_data = Column(
    LargeBinary,
    doc="Page json object - data, axes and metadata",
)

data property writable

data

estimator_id class-attribute instance-attribute

estimator_id = Column(
    Integer, ForeignKey("Estimator.id"), nullable=False
)

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

page_number class-attribute instance-attribute

page_number = Column(
    Integer, nullable=False, doc="Page number"
)

SimulationModel

Bases: Model

Simulation model

Source code in yaptide/persistence/models.py
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
class SimulationModel(db.Model):
    """Simulation model"""

    __tablename__ = 'Simulation'

    id: Column[int] = db.Column(db.Integer, primary_key=True)

    job_id: Column[str] = db.Column(db.String, nullable=False, unique=True, doc="Simulation job ID")

    user_id: Column[int] = db.Column(db.Integer, db.ForeignKey('User.id'), doc="User ID")
    start_time: Column[datetime] = db.Column(db.DateTime(timezone=True), default=now(), doc="Submission time")
    end_time: Column[datetime] = db.Column(db.DateTime(timezone=True),
                                           nullable=True,
                                           doc="Job end time (including merging)")
    title: Column[str] = db.Column(db.String, nullable=False, doc="Job title")
    platform: Column[str] = db.Column(db.String, nullable=False, doc="Execution platform name (i.e. 'direct', 'batch')")
    input_type: Column[str] = db.Column(db.String,
                                        nullable=False,
                                        doc="Input type (i.e. 'yaptide_project', 'input_files')")
    sim_type: Column[str] = db.Column(db.String,
                                      nullable=False,
                                      doc="Simulator type (i.e. 'shieldhit', 'topas', 'fluka')")
    job_state: Column[str] = db.Column(db.String,
                                       nullable=False,
                                       default=EntityState.UNKNOWN.value,
                                       doc="Simulation state (i.e. 'pending', 'running', 'completed', 'failed')")
    update_key_hash: Column[str] = db.Column(db.String,
                                             doc="Update key shared by tasks granting access to update themselves")
    tasks = relationship("TaskModel")
    estimators = relationship("EstimatorModel")

    __mapper_args__ = {"polymorphic_identity": "Simulation", "polymorphic_on": platform, "with_polymorphic": "*"}

    def set_update_key(self, update_key: str):
        """Sets hashed update key"""
        self.update_key_hash = generate_password_hash(update_key)

    def check_update_key(self, update_key: str) -> bool:
        """Checks update key correctness"""
        return check_password_hash(self.update_key_hash, update_key)

    def update_state(self, update_dict: dict) -> bool:
        """
        Updating database is more costly than a simple query.
        Therefore we check first if update is needed and
        perform it only for such fields which exists and which have updated values.
        Returns bool value telling if it is required to commit changes to db.
        """
        if self.job_state in (EntityState.COMPLETED.value, EntityState.FAILED.value, EntityState.CANCELED.value):
            return False
        db_commit_required = False
        if "job_state" in update_dict and self.job_state != update_dict["job_state"]:
            self.job_state = update_dict["job_state"]
            db_commit_required = True
        # Here we have a special case, `end_time` can be set only once
        # therefore we update it only if it not set previously (`self.end_time is None`)
        # and if update was requested (`"end_time" in update_dict`)
        if "end_time" in update_dict and self.end_time is None:
            # a convertion from string to datetime is needed, as in the POST payload end_time comes in string format
            self.end_time = datetime.strptime(update_dict["end_time"], '%Y-%m-%d %H:%M:%S.%f')
            db_commit_required = True
        return db_commit_required

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": "Simulation",
    "polymorphic_on": platform,
    "with_polymorphic": "*",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'Simulation'

end_time class-attribute instance-attribute

end_time = Column(
    DateTime(timezone=True),
    nullable=True,
    doc="Job end time (including merging)",
)

estimators class-attribute instance-attribute

estimators = relationship('EstimatorModel')

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

input_type class-attribute instance-attribute

input_type = Column(
    String,
    nullable=False,
    doc="Input type (i.e. 'yaptide_project', 'input_files')",
)

job_id class-attribute instance-attribute

job_id = Column(
    String,
    nullable=False,
    unique=True,
    doc="Simulation job ID",
)

job_state class-attribute instance-attribute

job_state = Column(
    String,
    nullable=False,
    default=value,
    doc="Simulation state (i.e. 'pending', 'running', 'completed', 'failed')",
)

platform class-attribute instance-attribute

platform = Column(
    String,
    nullable=False,
    doc="Execution platform name (i.e. 'direct', 'batch')",
)

sim_type class-attribute instance-attribute

sim_type = Column(
    String,
    nullable=False,
    doc="Simulator type (i.e. 'shieldhit', 'topas', 'fluka')",
)

start_time class-attribute instance-attribute

start_time = Column(
    DateTime(timezone=True),
    default=now(),
    doc="Submission time",
)

tasks class-attribute instance-attribute

tasks = relationship('TaskModel')

title class-attribute instance-attribute

title = Column(String, nullable=False, doc='Job title')

update_key_hash class-attribute instance-attribute

update_key_hash = Column(
    String,
    doc="Update key shared by tasks granting access to update themselves",
)

user_id class-attribute instance-attribute

user_id = Column(
    Integer, ForeignKey("User.id"), doc="User ID"
)

check_update_key

check_update_key(update_key)

Checks update key correctness

Source code in yaptide/persistence/models.py
106
107
108
def check_update_key(self, update_key: str) -> bool:
    """Checks update key correctness"""
    return check_password_hash(self.update_key_hash, update_key)

set_update_key

set_update_key(update_key)

Sets hashed update key

Source code in yaptide/persistence/models.py
102
103
104
def set_update_key(self, update_key: str):
    """Sets hashed update key"""
    self.update_key_hash = generate_password_hash(update_key)

update_state

update_state(update_dict)

Updating database is more costly than a simple query. Therefore we check first if update is needed and perform it only for such fields which exists and which have updated values. Returns bool value telling if it is required to commit changes to db.

Source code in yaptide/persistence/models.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def update_state(self, update_dict: dict) -> bool:
    """
    Updating database is more costly than a simple query.
    Therefore we check first if update is needed and
    perform it only for such fields which exists and which have updated values.
    Returns bool value telling if it is required to commit changes to db.
    """
    if self.job_state in (EntityState.COMPLETED.value, EntityState.FAILED.value, EntityState.CANCELED.value):
        return False
    db_commit_required = False
    if "job_state" in update_dict and self.job_state != update_dict["job_state"]:
        self.job_state = update_dict["job_state"]
        db_commit_required = True
    # Here we have a special case, `end_time` can be set only once
    # therefore we update it only if it not set previously (`self.end_time is None`)
    # and if update was requested (`"end_time" in update_dict`)
    if "end_time" in update_dict and self.end_time is None:
        # a convertion from string to datetime is needed, as in the POST payload end_time comes in string format
        self.end_time = datetime.strptime(update_dict["end_time"], '%Y-%m-%d %H:%M:%S.%f')
        db_commit_required = True
    return db_commit_required

TaskModel

Bases: Model

Simulation task model

Source code in yaptide/persistence/models.py
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
class TaskModel(db.Model):
    """Simulation task model"""

    __tablename__ = 'Task'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    simulation_id: Column[int] = db.Column(db.Integer,
                                           db.ForeignKey('Simulation.id'),
                                           doc="Simulation job ID (foreign key)")

    task_id: Column[int] = db.Column(db.Integer, nullable=False, doc="Task ID")
    requested_primaries: Column[int] = db.Column(db.Integer,
                                                 nullable=False,
                                                 default=0,
                                                 doc="Requested number of primaries")
    simulated_primaries: Column[int] = db.Column(db.Integer,
                                                 nullable=False,
                                                 default=0,
                                                 doc="Simulated number of primaries")
    task_state: Column[str] = db.Column(db.String,
                                        nullable=False,
                                        default=EntityState.PENDING.value,
                                        doc="Task state (i.e. 'pending', 'running', 'completed', 'failed')")
    estimated_time: Column[int] = db.Column(db.Integer, nullable=True, doc="Estimated time in seconds")
    start_time: Column[datetime] = db.Column(db.DateTime(timezone=True), nullable=True, doc="Task start time")
    end_time: Column[datetime] = db.Column(db.DateTime(timezone=True), nullable=True, doc="Task end time")
    platform: Column[str] = db.Column(db.String, nullable=False, doc="Execution platform name (i.e. 'direct', 'batch')")
    last_update_time: Column[datetime] = db.Column(db.DateTime(timezone=True),
                                                   default=now(),
                                                   doc="Task last update time")

    __table_args__ = (UniqueConstraint('simulation_id', 'task_id', name='_simulation_id_task_id_uc'), )

    __mapper_args__ = {"polymorphic_identity": "Task", "polymorphic_on": platform, "with_polymorphic": "*"}

    def update_state(self, update_dict: dict):
        """
        Updating database is more costly than a simple query.
        Therefore we check first if update is needed and
        perform it only for such fields which exists and which have updated values.
        """
        if self.task_state in (EntityState.COMPLETED.value, EntityState.FAILED.value, EntityState.CANCELED.value):
            return
        if "requested_primaries" in update_dict and self.requested_primaries != update_dict["requested_primaries"]:
            self.requested_primaries = update_dict["requested_primaries"]
        if "simulated_primaries" in update_dict and self.simulated_primaries != update_dict["simulated_primaries"]:
            self.simulated_primaries = update_dict["simulated_primaries"]
        if "task_state" in update_dict and self.task_state != update_dict["task_state"]:
            self.task_state = update_dict["task_state"]
        # Here we have a special case, `estimated_time` cannot be set when `end_time` is set - it is meaningless
        have_estim_time = "estimated_time" in update_dict and self.estimated_time != update_dict["estimated_time"]
        end_time_not_set = self.end_time is None
        if have_estim_time and end_time_not_set:
            self.estimated_time = update_dict["estimated_time"]
        if "start_time" in update_dict and self.start_time is None:
            # a convertion from string to datetime is needed, as in the POST payload start_time comes in string format
            self.start_time = datetime.strptime(update_dict["start_time"], '%Y-%m-%d %H:%M:%S.%f')
        # Here we have a special case, `end_time` can be set only once
        # therefore we update it only if it not set previously (`self.end_time is None`)
        # and if update was requested (`"end_time" in update_dict`)
        if "end_time" in update_dict and self.end_time is None:
            # a convertion from string to datetime is needed, as in the POST payload end_time comes in string format
            self.end_time = datetime.strptime(update_dict["end_time"], '%Y-%m-%d %H:%M:%S.%f')
            self.estimated_time = None
        self.last_update_time = now()

    def get_status_dict(self) -> dict:
        """Returns task information as a dictionary"""
        result = {
            "task_state": self.task_state,
            "requested_primaries": self.requested_primaries,
            "simulated_primaries": self.simulated_primaries,
            "last_update_time": self.last_update_time,
        }
        if self.estimated_time:
            result["estimated_time"] = {
                "hours": self.estimated_time // 3600,
                "minutes": (self.estimated_time // 60) % 60,
                "seconds": self.estimated_time % 60,
            }
        if self.start_time:
            result["start_time"] = self.start_time
        if self.end_time:
            result["end_time"] = self.end_time
        return result

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": "Task",
    "polymorphic_on": platform,
    "with_polymorphic": "*",
}

__table_args__ class-attribute instance-attribute

__table_args__ = UniqueConstraint(
    "simulation_id",
    "task_id",
    name="_simulation_id_task_id_uc",
)

__tablename__ class-attribute instance-attribute

__tablename__ = 'Task'

end_time class-attribute instance-attribute

end_time = Column(
    DateTime(timezone=True),
    nullable=True,
    doc="Task end time",
)

estimated_time class-attribute instance-attribute

estimated_time = Column(
    Integer, nullable=True, doc="Estimated time in seconds"
)

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

last_update_time class-attribute instance-attribute

last_update_time = Column(
    DateTime(timezone=True),
    default=now(),
    doc="Task last update time",
)

platform class-attribute instance-attribute

platform = Column(
    String,
    nullable=False,
    doc="Execution platform name (i.e. 'direct', 'batch')",
)

requested_primaries class-attribute instance-attribute

requested_primaries = Column(
    Integer,
    nullable=False,
    default=0,
    doc="Requested number of primaries",
)

simulated_primaries class-attribute instance-attribute

simulated_primaries = Column(
    Integer,
    nullable=False,
    default=0,
    doc="Simulated number of primaries",
)

simulation_id class-attribute instance-attribute

simulation_id = Column(
    Integer,
    ForeignKey("Simulation.id"),
    doc="Simulation job ID (foreign key)",
)

start_time class-attribute instance-attribute

start_time = Column(
    DateTime(timezone=True),
    nullable=True,
    doc="Task start time",
)

task_id class-attribute instance-attribute

task_id = Column(Integer, nullable=False, doc='Task ID')

task_state class-attribute instance-attribute

task_state = Column(
    String,
    nullable=False,
    default=value,
    doc="Task state (i.e. 'pending', 'running', 'completed', 'failed')",
)

get_status_dict

get_status_dict()

Returns task information as a dictionary

Source code in yaptide/persistence/models.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def get_status_dict(self) -> dict:
    """Returns task information as a dictionary"""
    result = {
        "task_state": self.task_state,
        "requested_primaries": self.requested_primaries,
        "simulated_primaries": self.simulated_primaries,
        "last_update_time": self.last_update_time,
    }
    if self.estimated_time:
        result["estimated_time"] = {
            "hours": self.estimated_time // 3600,
            "minutes": (self.estimated_time // 60) % 60,
            "seconds": self.estimated_time % 60,
        }
    if self.start_time:
        result["start_time"] = self.start_time
    if self.end_time:
        result["end_time"] = self.end_time
    return result

update_state

update_state(update_dict)

Updating database is more costly than a simple query. Therefore we check first if update is needed and perform it only for such fields which exists and which have updated values.

Source code in yaptide/persistence/models.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
def update_state(self, update_dict: dict):
    """
    Updating database is more costly than a simple query.
    Therefore we check first if update is needed and
    perform it only for such fields which exists and which have updated values.
    """
    if self.task_state in (EntityState.COMPLETED.value, EntityState.FAILED.value, EntityState.CANCELED.value):
        return
    if "requested_primaries" in update_dict and self.requested_primaries != update_dict["requested_primaries"]:
        self.requested_primaries = update_dict["requested_primaries"]
    if "simulated_primaries" in update_dict and self.simulated_primaries != update_dict["simulated_primaries"]:
        self.simulated_primaries = update_dict["simulated_primaries"]
    if "task_state" in update_dict and self.task_state != update_dict["task_state"]:
        self.task_state = update_dict["task_state"]
    # Here we have a special case, `estimated_time` cannot be set when `end_time` is set - it is meaningless
    have_estim_time = "estimated_time" in update_dict and self.estimated_time != update_dict["estimated_time"]
    end_time_not_set = self.end_time is None
    if have_estim_time and end_time_not_set:
        self.estimated_time = update_dict["estimated_time"]
    if "start_time" in update_dict and self.start_time is None:
        # a convertion from string to datetime is needed, as in the POST payload start_time comes in string format
        self.start_time = datetime.strptime(update_dict["start_time"], '%Y-%m-%d %H:%M:%S.%f')
    # Here we have a special case, `end_time` can be set only once
    # therefore we update it only if it not set previously (`self.end_time is None`)
    # and if update was requested (`"end_time" in update_dict`)
    if "end_time" in update_dict and self.end_time is None:
        # a convertion from string to datetime is needed, as in the POST payload end_time comes in string format
        self.end_time = datetime.strptime(update_dict["end_time"], '%Y-%m-%d %H:%M:%S.%f')
        self.estimated_time = None
    self.last_update_time = now()

UserModel

Bases: Model

User model

Source code in yaptide/persistence/models.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class UserModel(db.Model):
    """User model"""

    __tablename__ = 'User'
    id: Column[int] = db.Column(db.Integer, primary_key=True)
    username: Column[str] = db.Column(db.String, nullable=False)
    auth_provider: Column[str] = db.Column(db.String, nullable=False)
    simulations = relationship("SimulationModel")

    __table_args__ = (UniqueConstraint('username', 'auth_provider', name='_username_provider_uc'), )

    __mapper_args__ = {"polymorphic_identity": "User", "polymorphic_on": auth_provider, "with_polymorphic": "*"}

    def __repr__(self) -> str:
        return f'User #{self.id} {self.username}'

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": "User",
    "polymorphic_on": auth_provider,
    "with_polymorphic": "*",
}

__table_args__ class-attribute instance-attribute

__table_args__ = UniqueConstraint(
    "username",
    "auth_provider",
    name="_username_provider_uc",
)

__tablename__ class-attribute instance-attribute

__tablename__ = 'User'

auth_provider class-attribute instance-attribute

auth_provider = Column(String, nullable=False)

id class-attribute instance-attribute

id = Column(Integer, primary_key=True)

simulations class-attribute instance-attribute

simulations = relationship('SimulationModel')

username class-attribute instance-attribute

username = Column(String, nullable=False)

__repr__

__repr__()
Source code in yaptide/persistence/models.py
27
28
def __repr__(self) -> str:
    return f'User #{self.id} {self.username}'

YaptideUserModel

Bases: UserModel, Model

Yaptide user model

Source code in yaptide/persistence/models.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class YaptideUserModel(UserModel, db.Model):
    """Yaptide user model"""

    __tablename__ = 'YaptideUser'
    id: Column[int] = db.Column(db.Integer, db.ForeignKey('User.id', ondelete="CASCADE"), primary_key=True)
    password_hash: Column[str] = db.Column(db.String, nullable=False)

    __mapper_args__ = {"polymorphic_identity": "YaptideUser", "polymorphic_load": "inline"}

    def set_password(self, password: str):
        """Sets hashed password"""
        self.password_hash = generate_password_hash(password)

    def check_password(self, password: str) -> bool:
        """Checks password correctness"""
        return check_password_hash(self.password_hash, password)

__mapper_args__ class-attribute instance-attribute

__mapper_args__ = {
    "polymorphic_identity": "YaptideUser",
    "polymorphic_load": "inline",
}

__tablename__ class-attribute instance-attribute

__tablename__ = 'YaptideUser'

id class-attribute instance-attribute

id = Column(
    Integer,
    ForeignKey("User.id", ondelete="CASCADE"),
    primary_key=True,
)

password_hash class-attribute instance-attribute

password_hash = Column(String, nullable=False)

check_password

check_password(password)

Checks password correctness

Source code in yaptide/persistence/models.py
44
45
46
def check_password(self, password: str) -> bool:
    """Checks password correctness"""
    return check_password_hash(self.password_hash, password)

set_password

set_password(password)

Sets hashed password

Source code in yaptide/persistence/models.py
40
41
42
def set_password(self, password: str):
    """Sets hashed password"""
    self.password_hash = generate_password_hash(password)

compress

compress(data)

Serializes JSON and compresses data

Source code in yaptide/persistence/models.py
278
279
280
281
282
283
284
285
286
287
def compress(data) -> bytes:
    """Serializes JSON and compresses data"""
    compressed_bytes = b''
    if data is not None:
        # Serialize the JSON
        serialized_data: str = json.dumps(data)
        # Compress the data
        bytes_to_compress: bytes = serialized_data.encode('utf-8')
        compressed_bytes = gzip.compress(bytes_to_compress)
    return compressed_bytes

create_all

create_all()

Creates all tables, to be used with Flask app context.

Source code in yaptide/persistence/models.py
364
365
366
def create_all():
    """Creates all tables, to be used with Flask app context."""
    db.create_all()

decompress

decompress(data)

Decompresses data and deserializes JSON

Source code in yaptide/persistence/models.py
267
268
269
270
271
272
273
274
275
def decompress(data: bytes):
    """Decompresses data and deserializes JSON"""
    data_to_unpack: str = 'null'
    if data is not None:
        # Decompress the data
        decompressed_bytes: bytes = gzip.decompress(data)
        data_to_unpack = decompressed_bytes.decode('utf-8')
        # Deserialize the JSON
    return json.loads(data_to_unpack)