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.
Files changed (57) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +3 -1
  3. fractal_server/app/history/__init__.py +4 -4
  4. fractal_server/app/history/image_updates.py +124 -142
  5. fractal_server/app/history/status_enum.py +2 -2
  6. fractal_server/app/models/v2/__init__.py +6 -4
  7. fractal_server/app/models/v2/history.py +44 -20
  8. fractal_server/app/routes/admin/v2/task.py +1 -1
  9. fractal_server/app/routes/api/__init__.py +1 -1
  10. fractal_server/app/routes/api/v2/__init__.py +4 -0
  11. fractal_server/app/routes/api/v2/_aux_functions_history.py +49 -0
  12. fractal_server/app/routes/api/v2/dataset.py +0 -12
  13. fractal_server/app/routes/api/v2/history.py +302 -176
  14. fractal_server/app/routes/api/v2/project.py +1 -26
  15. fractal_server/app/routes/api/v2/status_legacy.py +168 -0
  16. fractal_server/app/routes/api/v2/workflow.py +2 -17
  17. fractal_server/app/routes/api/v2/workflowtask.py +41 -71
  18. fractal_server/app/routes/auth/oauth.py +5 -3
  19. fractal_server/app/runner/executors/base_runner.py +2 -1
  20. fractal_server/app/runner/executors/local/_submit_setup.py +5 -13
  21. fractal_server/app/runner/executors/local/runner.py +10 -55
  22. fractal_server/app/runner/executors/slurm_common/_slurm_config.py +1 -1
  23. fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +1 -1
  24. fractal_server/app/runner/executors/slurm_common/remote.py +1 -1
  25. fractal_server/app/runner/executors/slurm_sudo/runner.py +171 -108
  26. fractal_server/app/runner/v2/__init__.py +2 -22
  27. fractal_server/app/runner/v2/_slurm_ssh.py +1 -1
  28. fractal_server/app/runner/v2/_slurm_sudo.py +1 -1
  29. fractal_server/app/runner/v2/runner.py +47 -59
  30. fractal_server/app/runner/v2/runner_functions.py +185 -69
  31. fractal_server/app/schemas/_validators.py +13 -24
  32. fractal_server/app/schemas/user.py +10 -7
  33. fractal_server/app/schemas/user_settings.py +9 -21
  34. fractal_server/app/schemas/v2/dataset.py +8 -6
  35. fractal_server/app/schemas/v2/job.py +9 -5
  36. fractal_server/app/schemas/v2/manifest.py +3 -7
  37. fractal_server/app/schemas/v2/project.py +9 -7
  38. fractal_server/app/schemas/v2/task.py +41 -77
  39. fractal_server/app/schemas/v2/task_collection.py +14 -32
  40. fractal_server/app/schemas/v2/task_group.py +10 -9
  41. fractal_server/app/schemas/v2/workflow.py +10 -11
  42. fractal_server/app/security/__init__.py +3 -3
  43. fractal_server/app/security/signup_email.py +2 -2
  44. fractal_server/config.py +33 -34
  45. fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +120 -0
  46. fractal_server/tasks/v2/templates/2_pip_install.sh +1 -1
  47. fractal_server/tasks/v2/templates/4_pip_show.sh +1 -1
  48. fractal_server/tasks/v2/utils_templates.py +6 -0
  49. {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/METADATA +1 -1
  50. {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/RECORD +53 -54
  51. fractal_server/app/runner/executors/slurm_sudo/_executor_wait_thread.py +0 -130
  52. fractal_server/app/schemas/v2/history.py +0 -23
  53. fractal_server/migrations/versions/87cd72a537a2_add_historyitem_table.py +0 -68
  54. fractal_server/migrations/versions/954ddc64425a_image_status.py +0 -63
  55. {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/LICENSE +0 -0
  56. {fractal_server-2.14.0a2.dist-info → fractal_server-2.14.0a4.dist-info}/WHEEL +0 -0
  57. {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 ###