fractal-server 2.14.0a2__py3-none-any.whl → 2.14.0a4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractal_server/__init__.py +1 -1
- fractal_server/__main__.py +3 -1
- fractal_server/app/history/__init__.py +4 -4
- fractal_server/app/history/image_updates.py +124 -142
- fractal_server/app/history/status_enum.py +2 -2
- fractal_server/app/models/v2/__init__.py +6 -4
- fractal_server/app/models/v2/history.py +44 -20
- fractal_server/app/routes/admin/v2/task.py +1 -1
- fractal_server/app/routes/api/__init__.py +1 -1
- fractal_server/app/routes/api/v2/__init__.py +4 -0
- fractal_server/app/routes/api/v2/_aux_functions_history.py +49 -0
- fractal_server/app/routes/api/v2/dataset.py +0 -12
- fractal_server/app/routes/api/v2/history.py +302 -176
- fractal_server/app/routes/api/v2/project.py +1 -26
- fractal_server/app/routes/api/v2/status_legacy.py +168 -0
- fractal_server/app/routes/api/v2/workflow.py +2 -17
- fractal_server/app/routes/api/v2/workflowtask.py +41 -71
- fractal_server/app/routes/auth/oauth.py +5 -3
- fractal_server/app/runner/executors/base_runner.py +2 -1
- fractal_server/app/runner/executors/local/_submit_setup.py +5 -13
- fractal_server/app/runner/executors/local/runner.py +10 -55
- fractal_server/app/runner/executors/slurm_common/_slurm_config.py +1 -1
- fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +1 -1
- fractal_server/app/runner/executors/slurm_common/remote.py +1 -1
- fractal_server/app/runner/executors/slurm_sudo/runner.py +171 -108
- fractal_server/app/runner/v2/__init__.py +2 -22
- fractal_server/app/runner/v2/_slurm_ssh.py +1 -1
- fractal_server/app/runner/v2/_slurm_sudo.py +1 -1
- fractal_server/app/runner/v2/runner.py +47 -59
- fractal_server/app/runner/v2/runner_functions.py +185 -69
- fractal_server/app/schemas/_validators.py +13 -24
- fractal_server/app/schemas/user.py +10 -7
- fractal_server/app/schemas/user_settings.py +9 -21
- fractal_server/app/schemas/v2/dataset.py +8 -6
- fractal_server/app/schemas/v2/job.py +9 -5
- fractal_server/app/schemas/v2/manifest.py +3 -7
- fractal_server/app/schemas/v2/project.py +9 -7
- fractal_server/app/schemas/v2/task.py +41 -77
- fractal_server/app/schemas/v2/task_collection.py +14 -32
- fractal_server/app/schemas/v2/task_group.py +10 -9
- fractal_server/app/schemas/v2/workflow.py +10 -11
- fractal_server/app/security/__init__.py +3 -3
- fractal_server/app/security/signup_email.py +2 -2
- fractal_server/config.py +33 -34
- fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +120 -0
- fractal_server/tasks/v2/templates/2_pip_install.sh +1 -1
- fractal_server/tasks/v2/templates/4_pip_show.sh +1 -1
- fractal_server/tasks/v2/utils_templates.py +6 -0
- {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/METADATA +1 -1
- {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/RECORD +53 -54
- fractal_server/app/runner/executors/slurm_sudo/_executor_wait_thread.py +0 -130
- fractal_server/app/schemas/v2/history.py +0 -23
- fractal_server/migrations/versions/87cd72a537a2_add_historyitem_table.py +0 -68
- fractal_server/migrations/versions/954ddc64425a_image_status.py +0 -63
- {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/LICENSE +0 -0
- {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/WHEEL +0 -0
- {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/entry_points.txt +0 -0
@@ -1,130 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import threading
|
3
|
-
import time
|
4
|
-
import traceback
|
5
|
-
from itertools import count
|
6
|
-
from typing import Optional
|
7
|
-
|
8
|
-
from ._check_jobs_status import get_finished_jobs
|
9
|
-
from fractal_server.app.runner.exceptions import JobExecutionError
|
10
|
-
from fractal_server.logger import set_logger
|
11
|
-
|
12
|
-
logger = set_logger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
class FractalSlurmSudoWaitThread(threading.Thread):
|
16
|
-
"""
|
17
|
-
Thread that monitors a pool of SLURM jobs
|
18
|
-
|
19
|
-
This class is a custom re-implementation of the waiting thread class from:
|
20
|
-
|
21
|
-
> clusterfutures <https://github.com/sampsyo/clusterfutures>
|
22
|
-
> Original Copyright
|
23
|
-
> Copyright 2021 Adrian Sampson <asampson@cs.washington.edu>
|
24
|
-
> License: MIT
|
25
|
-
|
26
|
-
Attributes:
|
27
|
-
slurm_user:
|
28
|
-
shutdown_file:
|
29
|
-
shutdown_callback:
|
30
|
-
slurm_poll_interval:
|
31
|
-
waiting:
|
32
|
-
shutdown:
|
33
|
-
lock:
|
34
|
-
"""
|
35
|
-
|
36
|
-
slurm_user: str
|
37
|
-
shutdown_file: Optional[str] = None
|
38
|
-
shutdown_callback: callable
|
39
|
-
slurm_poll_interval: int = 30
|
40
|
-
waiting: dict[tuple[str, ...], str]
|
41
|
-
shutdown: bool
|
42
|
-
_lock: threading.Lock
|
43
|
-
|
44
|
-
def __init__(self, callback: callable, interval=1):
|
45
|
-
threading.Thread.__init__(self, daemon=True)
|
46
|
-
self.callback = callback
|
47
|
-
self.interval = interval
|
48
|
-
self.waiting = {}
|
49
|
-
self._lock = threading.Lock() # To protect the .waiting dict
|
50
|
-
self.shutdown = False
|
51
|
-
self.active_job_ids = []
|
52
|
-
|
53
|
-
def wait(
|
54
|
-
self,
|
55
|
-
*,
|
56
|
-
filenames: tuple[str, ...],
|
57
|
-
jobid: str,
|
58
|
-
):
|
59
|
-
"""
|
60
|
-
Add a a new job to the set of jobs being waited for.
|
61
|
-
|
62
|
-
A job consists of a tuple of filenames and a callback value (i.e. a
|
63
|
-
SLURM job ID).
|
64
|
-
|
65
|
-
Note that (with respect to clusterfutures) we replaced `filename` with
|
66
|
-
`filenames`.
|
67
|
-
"""
|
68
|
-
if self.shutdown:
|
69
|
-
error_msg = "Cannot call `wait` method after executor shutdown."
|
70
|
-
logger.warning(error_msg)
|
71
|
-
raise JobExecutionError(info=error_msg)
|
72
|
-
with self._lock:
|
73
|
-
self.waiting[filenames] = jobid
|
74
|
-
|
75
|
-
def check_shutdown(self, i):
|
76
|
-
"""
|
77
|
-
Do one shutdown-file-existence check.
|
78
|
-
|
79
|
-
Note: the `i` parameter allows subclasses like `SlurmWaitThread` to do
|
80
|
-
something on every Nth check.
|
81
|
-
|
82
|
-
Changed from clusterfutures:
|
83
|
-
* Do not check for output-pickle-file existence (we rather rely on
|
84
|
-
`cfut.slurm.jobs_finished`);
|
85
|
-
* Check for the existence of shutdown-file.
|
86
|
-
"""
|
87
|
-
if self.shutdown_file and os.path.exists(self.shutdown_file):
|
88
|
-
logger.info(
|
89
|
-
f"Detected executor-shutdown file {str(self.shutdown_file)}"
|
90
|
-
)
|
91
|
-
self.shutdown = True
|
92
|
-
|
93
|
-
def run(self):
|
94
|
-
"""
|
95
|
-
Overrides the original clusterfutures.FileWaitThread.run, adding a call
|
96
|
-
to self.shutdown_callback.
|
97
|
-
|
98
|
-
Changed from clusterfutures:
|
99
|
-
* We do not rely on output-file-existence checks to verify whether a
|
100
|
-
job is complete.
|
101
|
-
|
102
|
-
Note that `shutdown_callback` only takes care of cleaning up the
|
103
|
-
FractalSlurmExecutor variables, and then the `return` here is enough to
|
104
|
-
fully clean up the `FractalFileWaitThread` object.
|
105
|
-
"""
|
106
|
-
for i in count():
|
107
|
-
if self.shutdown:
|
108
|
-
self.shutdown_callback()
|
109
|
-
return
|
110
|
-
with self._lock:
|
111
|
-
self.check(i)
|
112
|
-
time.sleep(self.interval)
|
113
|
-
|
114
|
-
def check(self, i):
|
115
|
-
self.check_shutdown(i)
|
116
|
-
if i % (self.slurm_poll_interval // self.interval) == 0:
|
117
|
-
try:
|
118
|
-
finished_jobs = get_finished_jobs(self.waiting.values())
|
119
|
-
except Exception:
|
120
|
-
# Don't abandon completion checking if jobs_finished errors
|
121
|
-
traceback.print_exc()
|
122
|
-
return
|
123
|
-
|
124
|
-
if not finished_jobs:
|
125
|
-
return
|
126
|
-
|
127
|
-
id_to_filenames = {v: k for (k, v) in self.waiting.items()}
|
128
|
-
for finished_id in finished_jobs:
|
129
|
-
self.callback(finished_id)
|
130
|
-
self.waiting.pop(id_to_filenames[finished_id])
|
@@ -1,23 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
5
|
-
from pydantic import field_serializer
|
6
|
-
from pydantic.types import AwareDatetime
|
7
|
-
|
8
|
-
|
9
|
-
class HistoryItemV2Read(BaseModel):
|
10
|
-
id: int
|
11
|
-
dataset_id: int
|
12
|
-
workflowtask_id: int
|
13
|
-
timestamp_started: AwareDatetime
|
14
|
-
parameters_hash: str
|
15
|
-
num_available_images: int
|
16
|
-
num_current_images: int
|
17
|
-
images: dict[str, str]
|
18
|
-
workflowtask_dump: dict[str, Any]
|
19
|
-
task_group_dump: dict[str, Any]
|
20
|
-
|
21
|
-
@field_serializer("timestamp_started")
|
22
|
-
def serialize_datetime(v: datetime) -> str:
|
23
|
-
return v.isoformat()
|
@@ -1,68 +0,0 @@
|
|
1
|
-
"""Add HistoryItem table
|
2
|
-
|
3
|
-
Revision ID: 87cd72a537a2
|
4
|
-
Revises: af1ef1c83c9b
|
5
|
-
Create Date: 2025-02-18 10:48:16.401995
|
6
|
-
|
7
|
-
"""
|
8
|
-
import sqlalchemy as sa
|
9
|
-
import sqlmodel
|
10
|
-
from alembic import op
|
11
|
-
from sqlalchemy.dialects import postgresql
|
12
|
-
|
13
|
-
# revision identifiers, used by Alembic.
|
14
|
-
revision = "87cd72a537a2"
|
15
|
-
down_revision = "af1ef1c83c9b"
|
16
|
-
branch_labels = None
|
17
|
-
depends_on = None
|
18
|
-
|
19
|
-
|
20
|
-
def upgrade() -> None:
|
21
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
22
|
-
op.create_table(
|
23
|
-
"historyitemv2",
|
24
|
-
sa.Column("id", sa.Integer(), nullable=False),
|
25
|
-
sa.Column("dataset_id", sa.Integer(), nullable=False),
|
26
|
-
sa.Column("workflowtask_id", sa.Integer(), nullable=True),
|
27
|
-
sa.Column(
|
28
|
-
"timestamp_started", sa.DateTime(timezone=True), nullable=False
|
29
|
-
),
|
30
|
-
sa.Column(
|
31
|
-
"workflowtask_dump",
|
32
|
-
postgresql.JSONB(astext_type=sa.Text()),
|
33
|
-
nullable=False,
|
34
|
-
),
|
35
|
-
sa.Column(
|
36
|
-
"task_group_dump",
|
37
|
-
postgresql.JSONB(astext_type=sa.Text()),
|
38
|
-
nullable=False,
|
39
|
-
),
|
40
|
-
sa.Column(
|
41
|
-
"parameters_hash",
|
42
|
-
sqlmodel.sql.sqltypes.AutoString(),
|
43
|
-
nullable=False,
|
44
|
-
),
|
45
|
-
sa.Column("num_available_images", sa.Integer(), nullable=False),
|
46
|
-
sa.Column("num_current_images", sa.Integer(), nullable=False),
|
47
|
-
sa.Column(
|
48
|
-
"images", postgresql.JSONB(astext_type=sa.Text()), nullable=False
|
49
|
-
),
|
50
|
-
sa.ForeignKeyConstraint(
|
51
|
-
["dataset_id"],
|
52
|
-
["datasetv2.id"],
|
53
|
-
name=op.f("fk_historyitemv2_dataset_id_datasetv2"),
|
54
|
-
),
|
55
|
-
sa.ForeignKeyConstraint(
|
56
|
-
["workflowtask_id"],
|
57
|
-
["workflowtaskv2.id"],
|
58
|
-
name=op.f("fk_historyitemv2_workflowtask_id_workflowtaskv2"),
|
59
|
-
),
|
60
|
-
sa.PrimaryKeyConstraint("id", name=op.f("pk_historyitemv2")),
|
61
|
-
)
|
62
|
-
# ### end Alembic commands ###
|
63
|
-
|
64
|
-
|
65
|
-
def downgrade() -> None:
|
66
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
67
|
-
op.drop_table("historyitemv2")
|
68
|
-
# ### end Alembic commands ###
|
@@ -1,63 +0,0 @@
|
|
1
|
-
"""image status
|
2
|
-
|
3
|
-
Revision ID: 954ddc64425a
|
4
|
-
Revises: 87cd72a537a2
|
5
|
-
Create Date: 2025-02-28 16:37:38.765883
|
6
|
-
|
7
|
-
"""
|
8
|
-
import sqlalchemy as sa
|
9
|
-
import sqlmodel
|
10
|
-
from alembic import op
|
11
|
-
|
12
|
-
|
13
|
-
# revision identifiers, used by Alembic.
|
14
|
-
revision = "954ddc64425a"
|
15
|
-
down_revision = "87cd72a537a2"
|
16
|
-
branch_labels = None
|
17
|
-
depends_on = None
|
18
|
-
|
19
|
-
|
20
|
-
def upgrade() -> None:
|
21
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
22
|
-
op.create_table(
|
23
|
-
"imagestatus",
|
24
|
-
sa.Column(
|
25
|
-
"zarr_url", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
26
|
-
),
|
27
|
-
sa.Column("workflowtask_id", sa.Integer(), nullable=False),
|
28
|
-
sa.Column("dataset_id", sa.Integer(), nullable=False),
|
29
|
-
sa.Column(
|
30
|
-
"parameters_hash",
|
31
|
-
sqlmodel.sql.sqltypes.AutoString(),
|
32
|
-
nullable=False,
|
33
|
-
),
|
34
|
-
sa.Column(
|
35
|
-
"status", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
36
|
-
),
|
37
|
-
sa.Column(
|
38
|
-
"logfile", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
39
|
-
),
|
40
|
-
sa.ForeignKeyConstraint(
|
41
|
-
["dataset_id"],
|
42
|
-
["datasetv2.id"],
|
43
|
-
name=op.f("fk_imagestatus_dataset_id_datasetv2"),
|
44
|
-
),
|
45
|
-
sa.ForeignKeyConstraint(
|
46
|
-
["workflowtask_id"],
|
47
|
-
["workflowtaskv2.id"],
|
48
|
-
name=op.f("fk_imagestatus_workflowtask_id_workflowtaskv2"),
|
49
|
-
),
|
50
|
-
sa.PrimaryKeyConstraint(
|
51
|
-
"zarr_url",
|
52
|
-
"workflowtask_id",
|
53
|
-
"dataset_id",
|
54
|
-
name=op.f("pk_imagestatus"),
|
55
|
-
),
|
56
|
-
)
|
57
|
-
# ### end Alembic commands ###
|
58
|
-
|
59
|
-
|
60
|
-
def downgrade() -> None:
|
61
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
62
|
-
op.drop_table("imagestatus")
|
63
|
-
# ### end Alembic commands ###
|
File without changes
|
File without changes
|
File without changes
|