fractal-server 2.8.1__py3-none-any.whl → 2.9.0a0__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/app/models/v2/__init__.py +3 -3
- fractal_server/app/models/v2/task.py +0 -72
- fractal_server/app/models/v2/task_group.py +102 -0
- fractal_server/app/routes/admin/v1.py +1 -20
- fractal_server/app/routes/admin/v2/job.py +1 -20
- fractal_server/app/routes/admin/v2/task_group.py +53 -13
- fractal_server/app/routes/api/v2/__init__.py +11 -2
- fractal_server/app/routes/api/v2/{_aux_functions_task_collection.py → _aux_functions_task_lifecycle.py} +43 -0
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +21 -14
- fractal_server/app/routes/api/v2/task_collection.py +26 -51
- fractal_server/app/routes/api/v2/task_collection_custom.py +3 -3
- fractal_server/app/routes/api/v2/task_group.py +83 -14
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +221 -0
- fractal_server/app/routes/api/v2/workflow.py +1 -1
- fractal_server/app/routes/api/v2/workflow_import.py +2 -2
- fractal_server/app/routes/aux/_timestamp.py +25 -0
- fractal_server/app/schemas/v2/__init__.py +3 -2
- fractal_server/app/schemas/v2/task_collection.py +0 -21
- fractal_server/app/schemas/v2/task_group.py +30 -6
- fractal_server/migrations/versions/3082479ac4ea_taskgroup_activity_and_venv_info_to_.py +105 -0
- fractal_server/ssh/_fabric.py +18 -0
- fractal_server/tasks/utils.py +2 -12
- fractal_server/tasks/v2/local/__init__.py +3 -0
- fractal_server/tasks/v2/local/collect.py +291 -0
- fractal_server/tasks/v2/local/deactivate.py +162 -0
- fractal_server/tasks/v2/local/reactivate.py +159 -0
- fractal_server/tasks/v2/local/utils_local.py +52 -0
- fractal_server/tasks/v2/ssh/__init__.py +0 -0
- fractal_server/tasks/v2/ssh/collect.py +387 -0
- fractal_server/tasks/v2/ssh/deactivate.py +2 -0
- fractal_server/tasks/v2/ssh/reactivate.py +2 -0
- fractal_server/tasks/v2/templates/{_2_preliminary_pip_operations.sh → 1_create_venv.sh} +6 -7
- fractal_server/tasks/v2/templates/{_3_pip_install.sh → 2_pip_install.sh} +8 -1
- fractal_server/tasks/v2/templates/{_4_pip_freeze.sh → 3_pip_freeze.sh} +0 -7
- fractal_server/tasks/v2/templates/{_5_pip_show.sh → 4_pip_show.sh} +5 -6
- fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +10 -0
- fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +35 -0
- fractal_server/tasks/v2/utils_background.py +42 -103
- fractal_server/tasks/v2/utils_templates.py +32 -2
- fractal_server/utils.py +4 -2
- {fractal_server-2.8.1.dist-info → fractal_server-2.9.0a0.dist-info}/METADATA +2 -2
- {fractal_server-2.8.1.dist-info → fractal_server-2.9.0a0.dist-info}/RECORD +47 -36
- fractal_server/app/models/v2/collection_state.py +0 -22
- fractal_server/tasks/v2/collection_local.py +0 -357
- fractal_server/tasks/v2/collection_ssh.py +0 -352
- fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -42
- /fractal_server/tasks/v2/{database_operations.py → utils_database.py} +0 -0
- {fractal_server-2.8.1.dist-info → fractal_server-2.9.0a0.dist-info}/LICENSE +0 -0
- {fractal_server-2.8.1.dist-info → fractal_server-2.9.0a0.dist-info}/WHEEL +0 -0
- {fractal_server-2.8.1.dist-info → fractal_server-2.9.0a0.dist-info}/entry_points.txt +0 -0
@@ -11,7 +11,6 @@ PACKAGE_ENV_DIR=__PACKAGE_ENV_DIR__
|
|
11
11
|
PACKAGE_NAME=__PACKAGE_NAME__
|
12
12
|
|
13
13
|
|
14
|
-
|
15
14
|
TIME_START=$(date +%s)
|
16
15
|
|
17
16
|
VENVPYTHON=${PACKAGE_ENV_DIR}/bin/python
|
@@ -38,16 +37,16 @@ echo
|
|
38
37
|
MANIFEST_RELATIVE_PATH=$($VENVPYTHON -m pip show "$PACKAGE_NAME" --files | grep "__FRACTAL_MANIFEST__.json" | tr -d "[:space:]")
|
39
38
|
write_log "Manifest relative path: $MANIFEST_RELATIVE_PATH"
|
40
39
|
echo
|
41
|
-
|
42
|
-
write_log "Manifest absolute path: $MANIFEST_ABSOLUTE_PATH"
|
43
|
-
echo
|
44
|
-
if [ -f "$MANIFEST_ABSOLUTE_PATH" ]; then
|
40
|
+
if [ "$MANIFEST_RELATIVE_PATH" != "" ]; then
|
45
41
|
write_log "OK: manifest path exists"
|
46
42
|
echo
|
47
43
|
else
|
48
|
-
write_log "ERROR: manifest path not found
|
44
|
+
write_log "ERROR: manifest path not found for $PACKAGE_NAME"
|
49
45
|
exit 2
|
50
46
|
fi
|
47
|
+
MANIFEST_ABSOLUTE_PATH="${PACKAGE_PARENT_FOLDER}/${MANIFEST_RELATIVE_PATH}"
|
48
|
+
write_log "Manifest absolute path: $MANIFEST_ABSOLUTE_PATH"
|
49
|
+
echo
|
51
50
|
|
52
51
|
# End
|
53
52
|
TIME_END=$(date +%s)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
set -e
|
2
|
+
|
3
|
+
# Variables to be filled within fractal-server
|
4
|
+
PACKAGE_ENV_DIR=__PACKAGE_ENV_DIR__
|
5
|
+
|
6
|
+
# Find memory usage and file number
|
7
|
+
ENV_DISK_USAGE=$(du -sk "${PACKAGE_ENV_DIR}" | cut -f1)
|
8
|
+
ENV_FILE_NUMBER=$(find "${PACKAGE_ENV_DIR}" -type f | wc -l)
|
9
|
+
|
10
|
+
echo $ENV_DISK_USAGE $ENV_FILE_NUMBER
|
@@ -0,0 +1,35 @@
|
|
1
|
+
set -e
|
2
|
+
|
3
|
+
write_log(){
|
4
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
5
|
+
echo "[collect-task, $TIMESTAMP] $1"
|
6
|
+
}
|
7
|
+
|
8
|
+
# Variables to be filled within fractal-server
|
9
|
+
PACKAGE_ENV_DIR=__PACKAGE_ENV_DIR__
|
10
|
+
PIP_FREEZE_FILE=__PIP_FREEZE_FILE__
|
11
|
+
FRACTAL_MAX_PIP_VERSION=__FRACTAL_MAX_PIP_VERSION__
|
12
|
+
|
13
|
+
TIME_START=$(date +%s)
|
14
|
+
|
15
|
+
VENVPYTHON=${PACKAGE_ENV_DIR}/bin/python
|
16
|
+
|
17
|
+
# Upgrade `pip` and install `setuptools`
|
18
|
+
write_log "START upgrade pip and install setuptools"
|
19
|
+
"$VENVPYTHON" -m pip install --no-cache-dir "pip<=${FRACTAL_MAX_PIP_VERSION}" --upgrade
|
20
|
+
"$VENVPYTHON" -m pip install --no-cache-dir setuptools
|
21
|
+
write_log "END upgrade pip and install setuptools"
|
22
|
+
echo
|
23
|
+
|
24
|
+
# Install from pip-freeze file
|
25
|
+
write_log "START installing requirements from ${PIP_FREEZE_FILE}"
|
26
|
+
"$VENVPYTHON" -m pip install -r "${PIP_FREEZE_FILE}"
|
27
|
+
write_log "END installing requirements from ${PIP_FREEZE_FILE}"
|
28
|
+
echo
|
29
|
+
|
30
|
+
# End
|
31
|
+
TIME_END=$(date +%s)
|
32
|
+
write_log "All good up to here."
|
33
|
+
write_log "Elapsed: $((TIME_END - TIME_START)) seconds"
|
34
|
+
write_log "Exit."
|
35
|
+
echo
|
@@ -1,121 +1,70 @@
|
|
1
1
|
from pathlib import Path
|
2
2
|
from typing import Optional
|
3
|
+
from typing import TypeVar
|
3
4
|
|
4
5
|
from sqlalchemy.orm import Session as DBSyncSession
|
5
|
-
from sqlalchemy.orm.attributes import flag_modified
|
6
6
|
from sqlmodel import select
|
7
7
|
|
8
|
-
from fractal_server.app.models.v2 import
|
8
|
+
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
9
9
|
from fractal_server.app.models.v2 import TaskGroupV2
|
10
|
-
from fractal_server.app.schemas.v2 import CollectionStatusV2
|
11
10
|
from fractal_server.app.schemas.v2 import TaskCreateV2
|
11
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
12
12
|
from fractal_server.app.schemas.v2.manifest import ManifestV2
|
13
|
+
from fractal_server.app.schemas.v2.task_group import TaskGroupActivityActionV2
|
13
14
|
from fractal_server.logger import get_logger
|
14
15
|
from fractal_server.logger import reset_logger_handlers
|
16
|
+
from fractal_server.utils import get_timestamp
|
15
17
|
|
18
|
+
T = TypeVar("T")
|
16
19
|
|
17
|
-
def _set_collection_state_data_status(
|
18
|
-
*,
|
19
|
-
state_id: int,
|
20
|
-
new_status: CollectionStatusV2,
|
21
|
-
logger_name: str,
|
22
|
-
db: DBSyncSession,
|
23
|
-
):
|
24
|
-
logger = get_logger(logger_name)
|
25
|
-
logger.debug(f"{state_id=} - set state.data['status'] to {new_status}")
|
26
|
-
collection_state = db.get(CollectionStateV2, state_id)
|
27
|
-
collection_state.data["status"] = CollectionStatusV2(new_status)
|
28
|
-
flag_modified(collection_state, "data")
|
29
|
-
db.commit()
|
30
|
-
|
31
|
-
|
32
|
-
def _set_collection_state_data_log(
|
33
|
-
*,
|
34
|
-
state_id: int,
|
35
|
-
new_log: str,
|
36
|
-
logger_name: str,
|
37
|
-
db: DBSyncSession,
|
38
|
-
):
|
39
|
-
logger = get_logger(logger_name)
|
40
|
-
logger.debug(f"{state_id=} - set state.data['log']")
|
41
|
-
collection_state = db.get(CollectionStateV2, state_id)
|
42
|
-
collection_state.data["log"] = new_log
|
43
|
-
flag_modified(collection_state, "data")
|
44
|
-
db.commit()
|
45
20
|
|
46
|
-
|
47
|
-
|
48
|
-
*,
|
49
|
-
state_id: int,
|
50
|
-
new_info: str,
|
51
|
-
logger_name: str,
|
52
|
-
db: DBSyncSession,
|
53
|
-
):
|
54
|
-
logger = get_logger(logger_name)
|
55
|
-
logger.debug(f"{state_id=} - set state.data['info']")
|
56
|
-
collection_state = db.get(CollectionStateV2, state_id)
|
57
|
-
collection_state.data["info"] = new_info
|
58
|
-
flag_modified(collection_state, "data")
|
21
|
+
def add_commit_refresh(*, obj: T, db: DBSyncSession) -> T:
|
22
|
+
db.add(obj)
|
59
23
|
db.commit()
|
24
|
+
db.refresh(obj)
|
25
|
+
return obj
|
60
26
|
|
61
27
|
|
62
|
-
def
|
63
|
-
|
28
|
+
def fail_and_cleanup(
|
29
|
+
task_group: TaskGroupV2,
|
30
|
+
task_group_activity: TaskGroupActivityV2,
|
64
31
|
logger_name: str,
|
65
32
|
exception: Exception,
|
66
|
-
db: DBSyncSession,
|
67
|
-
task_group_id: int,
|
68
33
|
log_file_path: Path,
|
34
|
+
db: DBSyncSession,
|
69
35
|
):
|
70
36
|
logger = get_logger(logger_name)
|
71
|
-
logger.error(
|
72
|
-
|
73
|
-
|
74
|
-
state_id=state_id,
|
75
|
-
new_status=CollectionStatusV2.FAIL,
|
76
|
-
logger_name=logger_name,
|
77
|
-
db=db,
|
37
|
+
logger.error(
|
38
|
+
f"Task {task_group_activity.action} failed. "
|
39
|
+
f"Original error: {str(exception)}"
|
78
40
|
)
|
79
41
|
|
80
|
-
|
42
|
+
task_group_activity.status = TaskGroupActivityStatusV2.FAILED
|
43
|
+
task_group_activity.timestamp_ended = get_timestamp()
|
44
|
+
task_group_activity.log = get_current_log(log_file_path)
|
45
|
+
task_group_activity = add_commit_refresh(obj=task_group_activity, db=db)
|
46
|
+
if task_group_activity.action == TaskGroupActivityActionV2.COLLECT:
|
47
|
+
logger.info(f"Now delete TaskGroupV2 with {task_group.id=}")
|
81
48
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
logger_name=logger_name,
|
86
|
-
db=db,
|
87
|
-
)
|
88
|
-
# For backwards-compatibility, we also set state.data["info"]
|
89
|
-
_set_collection_state_data_info(
|
90
|
-
state_id=state_id,
|
91
|
-
new_info=f"Original error: {exception}",
|
92
|
-
logger_name=logger_name,
|
93
|
-
db=db,
|
94
|
-
)
|
95
|
-
|
96
|
-
# Delete TaskGroupV2 object / and apply cascade operation to FKs
|
97
|
-
logger.info(f"Now delete TaskGroupV2 with {task_group_id=}")
|
98
|
-
logger.info("Start of CollectionStateV2 cascade operations.")
|
99
|
-
stm = select(CollectionStateV2).where(
|
100
|
-
CollectionStateV2.taskgroupv2_id == task_group_id
|
101
|
-
)
|
102
|
-
res = db.execute(stm)
|
103
|
-
collection_states = res.scalars().all()
|
104
|
-
for collection_state in collection_states:
|
105
|
-
logger.info(
|
106
|
-
f"Setting CollectionStateV2[{collection_state.id}].taskgroupv2_id "
|
107
|
-
"to None."
|
49
|
+
logger.info("Start of TaskGroupActivityV2 cascade operations.")
|
50
|
+
stm = select(TaskGroupActivityV2).where(
|
51
|
+
TaskGroupActivityV2.taskgroupv2_id == task_group.id
|
108
52
|
)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
53
|
+
res = db.execute(stm)
|
54
|
+
task_group_activity_list = res.scalars().all()
|
55
|
+
for task_group_activity in task_group_activity_list:
|
56
|
+
logger.info(
|
57
|
+
f"Setting TaskGroupActivityV2[{task_group_activity.id}]"
|
58
|
+
".taskgroupv2_id to None."
|
59
|
+
)
|
60
|
+
task_group_activity.taskgroupv2_id = None
|
61
|
+
db.add(task_group_activity)
|
62
|
+
logger.info("End of TaskGroupActivityV2 cascade operations.")
|
63
|
+
logger.info(f"TaskGroupV2 with {task_group.id=} deleted")
|
116
64
|
|
65
|
+
db.delete(task_group)
|
66
|
+
db.commit()
|
117
67
|
reset_logger_handlers(logger)
|
118
|
-
return
|
119
68
|
|
120
69
|
|
121
70
|
def _prepare_tasks_metadata(
|
@@ -194,16 +143,6 @@ def check_task_files_exist(task_list: list[TaskCreateV2]) -> None:
|
|
194
143
|
)
|
195
144
|
|
196
145
|
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
log_file_path: Path,
|
201
|
-
db: DBSyncSession,
|
202
|
-
) -> None:
|
203
|
-
"""
|
204
|
-
Read logs from file and update them in the db.
|
205
|
-
"""
|
206
|
-
collection_state = db.get(CollectionStateV2, state_id)
|
207
|
-
collection_state.data["log"] = log_file_path.open("r").read()
|
208
|
-
flag_modified(collection_state, "data")
|
209
|
-
db.commit()
|
146
|
+
def get_current_log(logger_file_path: str) -> str:
|
147
|
+
with open(logger_file_path, "r") as f:
|
148
|
+
return f.read()
|
@@ -1,7 +1,13 @@
|
|
1
1
|
from pathlib import Path
|
2
2
|
|
3
|
+
from fractal_server.app.models.v2 import TaskGroupV2
|
4
|
+
from fractal_server.config import get_settings
|
5
|
+
from fractal_server.syringe import Inject
|
6
|
+
|
3
7
|
TEMPLATES_DIR = Path(__file__).parent / "templates"
|
4
8
|
|
9
|
+
SCRIPTS_SUBFOLDER = "scripts"
|
10
|
+
|
5
11
|
|
6
12
|
def customize_template(
|
7
13
|
*,
|
@@ -25,14 +31,16 @@ def customize_template(
|
|
25
31
|
script_data = template_data
|
26
32
|
for old_new in replacements:
|
27
33
|
script_data = script_data.replace(old_new[0], old_new[1])
|
34
|
+
# Create parent folder if needed
|
35
|
+
Path(script_path).parent.mkdir(exist_ok=True)
|
28
36
|
# Write script locally
|
29
37
|
with open(script_path, "w") as f:
|
30
38
|
f.write(script_data)
|
31
39
|
|
32
40
|
|
33
|
-
def
|
41
|
+
def parse_script_pip_show_stdout(stdout: str) -> dict[str, str]:
|
34
42
|
"""
|
35
|
-
Parse standard output of
|
43
|
+
Parse standard output of 4_pip_show.sh
|
36
44
|
"""
|
37
45
|
searches = [
|
38
46
|
("Python interpreter:", "python_bin"),
|
@@ -57,3 +65,25 @@ def parse_script_5_stdout(stdout: str) -> dict[str, str]:
|
|
57
65
|
attribute_value = actual_line.split(search)[-1].strip(" ")
|
58
66
|
attributes[attribute_name] = attribute_value
|
59
67
|
return attributes
|
68
|
+
|
69
|
+
|
70
|
+
def get_collection_replacements(
|
71
|
+
*, task_group: TaskGroupV2, python_bin: str
|
72
|
+
) -> dict[str, str]:
|
73
|
+
settings = Inject(get_settings)
|
74
|
+
|
75
|
+
replacements = [
|
76
|
+
("__PACKAGE_NAME__", task_group.pkg_name),
|
77
|
+
("__PACKAGE_ENV_DIR__", task_group.venv_path),
|
78
|
+
("__PYTHON__", python_bin),
|
79
|
+
("__INSTALL_STRING__", task_group.pip_install_string),
|
80
|
+
(
|
81
|
+
"__FRACTAL_MAX_PIP_VERSION__",
|
82
|
+
settings.FRACTAL_MAX_PIP_VERSION,
|
83
|
+
),
|
84
|
+
(
|
85
|
+
"__PINNED_PACKAGE_LIST__",
|
86
|
+
task_group.pinned_package_versions_string,
|
87
|
+
),
|
88
|
+
]
|
89
|
+
return replacements
|
fractal_server/utils.py
CHANGED
@@ -108,8 +108,10 @@ def execute_command_sync(
|
|
108
108
|
stdout = res.stdout
|
109
109
|
stderr = res.stderr
|
110
110
|
logger.debug(f"{returncode=}")
|
111
|
-
logger.debug(
|
112
|
-
logger.debug(
|
111
|
+
logger.debug("STDOUT:")
|
112
|
+
logger.debug(stdout)
|
113
|
+
logger.debug("STDERR:")
|
114
|
+
logger.debug(stderr)
|
113
115
|
if res.returncode != 0:
|
114
116
|
logger.debug(f"ERROR in subprocess call to '{command}'")
|
115
117
|
raise RuntimeError(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fractal-server
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.9.0a0
|
4
4
|
Summary: Server component of the Fractal analytics platform
|
5
5
|
Home-page: https://github.com/fractal-analytics-platform/fractal-server
|
6
6
|
License: BSD-3-Clause
|
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
|
|
38
38
|
# Fractal Server
|
39
39
|
|
40
40
|
<p align="center">
|
41
|
-
<img src="https://
|
41
|
+
<img src="https://raw.githubusercontent.com/fractal-analytics-platform/fractal-logos/refs/heads/main/projects/fractal_server.png" alt="Fractal server" width="400">
|
42
42
|
</p>
|
43
43
|
|
44
44
|
[](https://pypi.org/project/fractal-server/)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
fractal_server/__init__.py,sha256=
|
1
|
+
fractal_server/__init__.py,sha256=nUyCW0fhiPHMc-u4jKcBUavNOA93MoQcsdShypf4ins,24
|
2
2
|
fractal_server/__main__.py,sha256=dEkCfzLLQrIlxsGC-HBfoR-RBMWnJDgNrxYTyzmE9c0,6146
|
3
3
|
fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
|
4
4
|
fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -15,22 +15,22 @@ fractal_server/app/models/v1/project.py,sha256=JG7b5J9CzVNxua4MaMYpfB57xt2qjbXr5
|
|
15
15
|
fractal_server/app/models/v1/state.py,sha256=m9gMZqqnm3oDpJNJp-Lht4kM7oO7pcEI7sL1g7LFvWU,1043
|
16
16
|
fractal_server/app/models/v1/task.py,sha256=uFXam7eu3Ye1Yt7_g7llCzY8BetmDRilsq5hR2C1Zbg,2640
|
17
17
|
fractal_server/app/models/v1/workflow.py,sha256=dnY5eMaOe3oZv8arn00RNX9qVkBtTLG-vYdWXcQuyo4,3950
|
18
|
-
fractal_server/app/models/v2/__init__.py,sha256=
|
19
|
-
fractal_server/app/models/v2/collection_state.py,sha256=Yx18ZbywjraOdlHFyRVlb3VP101jBkOKkuOBIVD16fY,660
|
18
|
+
fractal_server/app/models/v2/__init__.py,sha256=63THGEZQlxWcosGCI74SEvJU7wOoOn1j1byTjf4NFOI,526
|
20
19
|
fractal_server/app/models/v2/dataset.py,sha256=-7sxHEw4IIAvF_uSan7tA3o8hvoakBkQ0SRvqS2iOQU,1455
|
21
20
|
fractal_server/app/models/v2/job.py,sha256=ypJmN-qspkKBGhBG7Mt-HypSQqcQ2EmB4Bzzb2-y550,1535
|
22
21
|
fractal_server/app/models/v2/project.py,sha256=rAHoh5KfYwIaW7rTX0_O0jvWmxEvfo1BafvmcXuSSRk,786
|
23
|
-
fractal_server/app/models/v2/task.py,sha256=
|
22
|
+
fractal_server/app/models/v2/task.py,sha256=jebD28Pz8tGcsWCItxj6uKjcD8BMMnnU8dqYhvhEB6c,1520
|
23
|
+
fractal_server/app/models/v2/task_group.py,sha256=X2Qlg-at2RcbvlWbpEMT3kZCk5WOVmJ6cOnKF-RDnQ0,3107
|
24
24
|
fractal_server/app/models/v2/workflow.py,sha256=YBgFGCziUgU0aJ5EM3Svu9W2c46AewZO9VBlFCHiSps,1069
|
25
25
|
fractal_server/app/models/v2/workflowtask.py,sha256=iDuJYk8kp4PNqGmbKRtGI7y-QsbjkNd_gDsbMzL4i-g,1274
|
26
26
|
fractal_server/app/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
fractal_server/app/routes/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
fractal_server/app/routes/admin/v1.py,sha256=
|
28
|
+
fractal_server/app/routes/admin/v1.py,sha256=5YxfWz2QK2spUKomnAteaMsWF9Ql-oHwMs1jhSjHznk,13319
|
29
29
|
fractal_server/app/routes/admin/v2/__init__.py,sha256=zkdrk3mSQWfgTJugS8Sc_Q_xCAwfmHUfdRe0DTaYT80,521
|
30
|
-
fractal_server/app/routes/admin/v2/job.py,sha256=
|
30
|
+
fractal_server/app/routes/admin/v2/job.py,sha256=DbkPmUk7V2iEGIlKa_xgC07cc3G8oNsONGDZ_aKq5zE,7589
|
31
31
|
fractal_server/app/routes/admin/v2/project.py,sha256=luy-yiGX1JYTdPm1hpIdDUUqPm8xHuipLy9k2X6zu74,1223
|
32
32
|
fractal_server/app/routes/admin/v2/task.py,sha256=Y0eujBgGhVapNXfW9azDxw4EBzLmEmCdh70y1RNQcb0,3895
|
33
|
-
fractal_server/app/routes/admin/v2/task_group.py,sha256=
|
33
|
+
fractal_server/app/routes/admin/v2/task_group.py,sha256=ldMQ8OIUKEhr1a_BXiwgh4K3d1T299ZaDNJjbn_zAxc,7411
|
34
34
|
fractal_server/app/routes/api/__init__.py,sha256=2IDheFi0OFdsUg7nbUiyahqybvpgXqeHUXIL2QtWrQQ,641
|
35
35
|
fractal_server/app/routes/api/v1/__init__.py,sha256=Y2HQdG197J0a7DyQEE2jn53IfxD0EHGhzK1I2JZuEck,958
|
36
36
|
fractal_server/app/routes/api/v1/_aux_functions.py,sha256=P9Q48thGH95w0h5cacYoibxqgiiLW4oqZ8rNJ2LIISY,13219
|
@@ -41,10 +41,10 @@ fractal_server/app/routes/api/v1/task.py,sha256=eW89nMCjpD4G6tHXDo2qGBKqWaPirjH6
|
|
41
41
|
fractal_server/app/routes/api/v1/task_collection.py,sha256=5EMh3yhS1Z4x25kp5Iaxalrf7RgJh-XD1nBjrFvgwsg,9072
|
42
42
|
fractal_server/app/routes/api/v1/workflow.py,sha256=2T93DuEnSshaDCue-JPmjuvGCtbk6lt9pFMuPt783t8,11217
|
43
43
|
fractal_server/app/routes/api/v1/workflowtask.py,sha256=OYYConwJbmNULDw5I3T-UbSJKrbbBiAHbbBeVcpoFKQ,5785
|
44
|
-
fractal_server/app/routes/api/v2/__init__.py,sha256=
|
44
|
+
fractal_server/app/routes/api/v2/__init__.py,sha256=w4c9WzagaVV5d4TWBX5buu5ENk8jf3YftMQYmhavz9Q,2172
|
45
45
|
fractal_server/app/routes/api/v2/_aux_functions.py,sha256=mb4R_qqFxeW0LAis2QJIIfVx8Sydv1jTYaRIMsMxnIk,11720
|
46
|
-
fractal_server/app/routes/api/v2/
|
47
|
-
fractal_server/app/routes/api/v2/_aux_functions_tasks.py,sha256=
|
46
|
+
fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py,sha256=04-C7R8aWiXN9-rSTnZIqz7CAGcLm0y9bjIgJ2lHGZw,5332
|
47
|
+
fractal_server/app/routes/api/v2/_aux_functions_tasks.py,sha256=kOoLy5a1YwgfSgsbnCVm_pc5DH6oiG0SU0k-pVDeOY0,10947
|
48
48
|
fractal_server/app/routes/api/v2/dataset.py,sha256=Y6uZz--YSEGgnPYu05rZ9sr1Ug08bNl2v1h3VeApBe8,9441
|
49
49
|
fractal_server/app/routes/api/v2/images.py,sha256=JR1rR6qEs81nacjriOXAOBQjAbCXF4Ew7M7mkWdxBU0,7920
|
50
50
|
fractal_server/app/routes/api/v2/job.py,sha256=Bga2Kz1OjvDIdxZObWaaXVhNIhC_5JKhKRjEH2_ayEE,5157
|
@@ -52,11 +52,12 @@ fractal_server/app/routes/api/v2/project.py,sha256=eWYFJ7F2ZYQcpi-_n-rhPF-Q4gJhz
|
|
52
52
|
fractal_server/app/routes/api/v2/status.py,sha256=6N9DSZ4iFqbZImorWfEAPoyoFUgEruo4Hweqo0x0xXU,6435
|
53
53
|
fractal_server/app/routes/api/v2/submit.py,sha256=tq-NGnUlpIcm_MRN47rJRHkRcIJ5HiL4Wj1wItJy3o8,8185
|
54
54
|
fractal_server/app/routes/api/v2/task.py,sha256=K0ik33t7vL8BAK5S7fqyJDNdRK4stGqb_73bSa8tvPE,7159
|
55
|
-
fractal_server/app/routes/api/v2/task_collection.py,sha256=
|
56
|
-
fractal_server/app/routes/api/v2/task_collection_custom.py,sha256=
|
57
|
-
fractal_server/app/routes/api/v2/task_group.py,sha256=
|
58
|
-
fractal_server/app/routes/api/v2/
|
59
|
-
fractal_server/app/routes/api/v2/
|
55
|
+
fractal_server/app/routes/api/v2/task_collection.py,sha256=1Jch0xRdcb--al6Oigk5fEMIOcIvNwa_aidFEHCd-FQ,9852
|
56
|
+
fractal_server/app/routes/api/v2/task_collection_custom.py,sha256=cctW61-C2QYF2KXluS15lLhZJS_kt30Ca6UGLFO32z0,6207
|
57
|
+
fractal_server/app/routes/api/v2/task_group.py,sha256=Ove7Vr3p8GKtskHANAZh8TWwOw8dfbFCN2UQFv6DhqM,8136
|
58
|
+
fractal_server/app/routes/api/v2/task_group_lifecycle.py,sha256=UODAv1kk8qgT7wvcVKK6eRs-fEBw3T1pPwmT6VkkAn4,7404
|
59
|
+
fractal_server/app/routes/api/v2/workflow.py,sha256=vjCNRzMHaAB4YWbAEWGlELHXDN4GjtE26IkIiB15RGM,8682
|
60
|
+
fractal_server/app/routes/api/v2/workflow_import.py,sha256=WJST1AZypvOTGUrjhomYVh4R2ow8RoGpuwzNiq81Pzc,10971
|
60
61
|
fractal_server/app/routes/api/v2/workflowtask.py,sha256=ciHTwXXFiFnMF7ZpJ3Xs0q6YfuZrFvIjqndlzAEdZpo,6969
|
61
62
|
fractal_server/app/routes/auth/__init__.py,sha256=fao6CS0WiAjHDTvBzgBVV_bSXFpEAeDBF6Z6q7rRkPc,1658
|
62
63
|
fractal_server/app/routes/auth/_aux_auth.py,sha256=ifkNocTYatBSMYGwiR14qohmvR9SfMldceiEj6uJBrU,4783
|
@@ -70,6 +71,7 @@ fractal_server/app/routes/auth/users.py,sha256=FzKNoB-wD32AkVOj1Vi29lGGyOl8NSMCR
|
|
70
71
|
fractal_server/app/routes/aux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
72
|
fractal_server/app/routes/aux/_job.py,sha256=q-RCiW17yXnZKAC_0La52RLvhqhxuvbgQJ2MlGXOj8A,702
|
72
73
|
fractal_server/app/routes/aux/_runner.py,sha256=FdCVla5DxGAZ__aB7Z8dEJzD_RIeh5tftjrPyqkr8N8,895
|
74
|
+
fractal_server/app/routes/aux/_timestamp.py,sha256=t3X1WQIGZVAf0snxZzUeOi9Gh4zQbxmiYPGc9NeuLHA,850
|
73
75
|
fractal_server/app/routes/aux/validate_user_settings.py,sha256=Y8eubau0julkwVYB5nA83nDtxh_7RU9Iq0zAhb_dXLA,2351
|
74
76
|
fractal_server/app/runner/.gitignore,sha256=ytzN_oyHWXrGU7iFAtoHSTUbM6Rn6kG0Zkddg0xZk6s,16
|
75
77
|
fractal_server/app/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -146,7 +148,7 @@ fractal_server/app/schemas/v1/state.py,sha256=GYeOE_1PtDOgu5W4t_3gw3DBHXH2aCGzIN
|
|
146
148
|
fractal_server/app/schemas/v1/task.py,sha256=7BxOZ_qoRQ8n3YbQpDvB7VMcxB5fSYQmR5RLIWhuJ5U,3704
|
147
149
|
fractal_server/app/schemas/v1/task_collection.py,sha256=uvq9bcMaGD_qHsh7YtcpoSAkVAbw12eY4DocIO3MKOg,3057
|
148
150
|
fractal_server/app/schemas/v1/workflow.py,sha256=tuOs5E5Q_ozA8if7YPZ07cQjzqB_QMkBS4u92qo4Ro0,4618
|
149
|
-
fractal_server/app/schemas/v2/__init__.py,sha256=
|
151
|
+
fractal_server/app/schemas/v2/__init__.py,sha256=dzDsAzLLMgyQBa3b64n71K1u83tbBn9_Oloaqqv1tjA,2444
|
150
152
|
fractal_server/app/schemas/v2/dataset.py,sha256=Jipcj9LiOOipAeM2Ew113wuNQ6CrbC1nf3KwnNApBco,2638
|
151
153
|
fractal_server/app/schemas/v2/dumps.py,sha256=s6dg-pHZFui6t2Ktm0SMxjKDN-v-ZqBHz9iTsBQF3eU,1712
|
152
154
|
fractal_server/app/schemas/v2/job.py,sha256=oYSLYkQ0HL83QyjEGIaggtZ117FndzFlONMKWd9sTXM,3270
|
@@ -154,8 +156,8 @@ fractal_server/app/schemas/v2/manifest.py,sha256=Uqtd7DbyOkf9bxBOKkU7Sv7nToBIFGU
|
|
154
156
|
fractal_server/app/schemas/v2/project.py,sha256=UXEA0UUUe0bFFOVLLmVtvDFLBO5vmD1JVI7EeTIcwDo,756
|
155
157
|
fractal_server/app/schemas/v2/status.py,sha256=SQaUpQkjFq5c5k5J4rOjNhuQaDOEg8lksPhkKmPU5VU,332
|
156
158
|
fractal_server/app/schemas/v2/task.py,sha256=FFAbYwDlqowB8gVMdjFVPVHvAM0T89PYLixUth49xfQ,6870
|
157
|
-
fractal_server/app/schemas/v2/task_collection.py,sha256=
|
158
|
-
fractal_server/app/schemas/v2/task_group.py,sha256=
|
159
|
+
fractal_server/app/schemas/v2/task_collection.py,sha256=yHpCRxoj6tKqCiQfUjaTj8SfCn1ChD_P6okfEOzyUDE,6518
|
160
|
+
fractal_server/app/schemas/v2/task_group.py,sha256=0eOjQ1PjvxxHQULX1f6gDGhm30BtKW6k5CcRaSUaQtg,3007
|
159
161
|
fractal_server/app/schemas/v2/workflow.py,sha256=HSNQSrBRdoBzh8Igr76FUWCAWvVzykrqmUv1vGv-8og,2026
|
160
162
|
fractal_server/app/schemas/v2/workflowtask.py,sha256=vDdMktYbHeYBgB5OuWSv6wRPRXWqvetkeqQ7IC5YtfA,5751
|
161
163
|
fractal_server/app/security/__init__.py,sha256=8Xd4GxumZgvxEH1Vli3ULehwdesEPiaAbtffJvAEgNo,12509
|
@@ -176,6 +178,7 @@ fractal_server/migrations/script.py.mako,sha256=oMXw9LC3zRbinWWPPDgeZ4z9FJrV2zhR
|
|
176
178
|
fractal_server/migrations/versions/034a469ec2eb_task_groups.py,sha256=vrPhC8hfFu1c4HmLHNZyCuqEfecFD8-bWc49bXMNes0,6199
|
177
179
|
fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py,sha256=-BSS9AFTPcu3gYC-sYbawSy4MWQQx8TfMb5BW5EBKmQ,1450
|
178
180
|
fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py,sha256=Q1Gj1cJ0UrdLBJ5AXfFK9QpxTtmcv-4Z3NEGDnxOme4,961
|
181
|
+
fractal_server/migrations/versions/3082479ac4ea_taskgroup_activity_and_venv_info_to_.py,sha256=oAqSwW9ilKm5x6Wez3JpJFU2-ls2_O8-j1R6KLEir08,3411
|
179
182
|
fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py,sha256=-wHe-fOffmYeAm0JXVl_lxZ7hhDkaEVqxgxpHkb_uL8,954
|
180
183
|
fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py,sha256=Mob8McGYAcmgvrseyyYOa54E6Gsgr-4SiGdC-r9O4_A,1157
|
181
184
|
fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py,sha256=5ROUgcoZOdjf8kMt6cxuvPhzHmV6xaCxvZEbhUEyZM4,3271
|
@@ -199,11 +202,11 @@ fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py
|
|
199
202
|
fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py,sha256=9BwqUS9Gf7UW_KjrzHbtViC880qhD452KAytkHWWZyk,746
|
200
203
|
fractal_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
201
204
|
fractal_server/ssh/__init__.py,sha256=sVUmzxf7_DuXG1xoLQ1_00fo5NPhi2LJipSmU5EAkPs,124
|
202
|
-
fractal_server/ssh/_fabric.py,sha256=
|
205
|
+
fractal_server/ssh/_fabric.py,sha256=udbz8BsbToqf2J_JjSlGUgVQGcP0tY-TjG2plzPoGPM,20180
|
203
206
|
fractal_server/string_tools.py,sha256=XtMNsr5R7GmgzmFi68zkKMedHs8vjGoVMMCXqWhIk9k,2568
|
204
207
|
fractal_server/syringe.py,sha256=3qSMW3YaMKKnLdgnooAINOPxnCOxP7y2jeAQYB21Gdo,2786
|
205
208
|
fractal_server/tasks/__init__.py,sha256=kadmVUoIghl8s190_Tt-8f-WBqMi8u8oU4Pvw39NHE8,23
|
206
|
-
fractal_server/tasks/utils.py,sha256=
|
209
|
+
fractal_server/tasks/utils.py,sha256=2ShlUSOXx53IiZT72dTCF31xXoLX9P8bY18vMj2m-mc,1059
|
207
210
|
fractal_server/tasks/v1/_TaskCollectPip.py,sha256=ARq5AoHYXH0hziEsb-nFAqbsLA-VIddXOdXL38O6_zA,3746
|
208
211
|
fractal_server/tasks/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
209
212
|
fractal_server/tasks/v1/background_operations.py,sha256=0Zm8TT_RV0BxJCXbruYJCu1eXOkEcHpqnWn_BEe7huw,11829
|
@@ -211,23 +214,31 @@ fractal_server/tasks/v1/endpoint_operations.py,sha256=NQYvgh-_qEI9YhsLiulfOFPDac
|
|
211
214
|
fractal_server/tasks/v1/get_collection_data.py,sha256=5C22jp356rCH5IIC0J57wOu-DCC_kp3B6p68JooN7IM,508
|
212
215
|
fractal_server/tasks/v1/utils.py,sha256=HYFyNAyZofmf--mVgdwGC5TJpGShIWIDaS01yRr4HxM,1771
|
213
216
|
fractal_server/tasks/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
214
|
-
fractal_server/tasks/v2/
|
215
|
-
fractal_server/tasks/v2/
|
216
|
-
fractal_server/tasks/v2/
|
217
|
-
fractal_server/tasks/v2/
|
218
|
-
fractal_server/tasks/v2/
|
219
|
-
fractal_server/tasks/v2/
|
220
|
-
fractal_server/tasks/v2/
|
221
|
-
fractal_server/tasks/v2/
|
222
|
-
fractal_server/tasks/v2/
|
217
|
+
fractal_server/tasks/v2/local/__init__.py,sha256=taFip1YHvnsfTlReqwZsdo4EmIq7hlAl2WV1g3Vp_TY,149
|
218
|
+
fractal_server/tasks/v2/local/collect.py,sha256=qUSKpI5h5qJjzwXtezEzzo9jIHfXR8mbWCgDCJ4Huyc,12207
|
219
|
+
fractal_server/tasks/v2/local/deactivate.py,sha256=f3j-a5npujZ5lsCBI_f39bKR7n2n79iSprmgm7_FJYk,6437
|
220
|
+
fractal_server/tasks/v2/local/reactivate.py,sha256=eTSrEoZ54_6ExviozxRTgjNsrESG9PbQW-TiGqNMJcA,6117
|
221
|
+
fractal_server/tasks/v2/local/utils_local.py,sha256=987EzL2BByPszoTc2dP-ucZigXp7fRdbBffeFGzbtPw,1785
|
222
|
+
fractal_server/tasks/v2/ssh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
223
|
+
fractal_server/tasks/v2/ssh/collect.py,sha256=IwsA8Xr0d6VXvajdFxwNIEB6jZ--hA1McEu6tydKq9E,15341
|
224
|
+
fractal_server/tasks/v2/ssh/deactivate.py,sha256=fwBCtj-8I_8s8Zd-nyQX7YaTjoPOK1pn07Tznvjbv8Y,31
|
225
|
+
fractal_server/tasks/v2/ssh/reactivate.py,sha256=0pqK-g5uyykCkLsjYDVr8QTEBrIB31XQXvi0MQfqv-w,31
|
226
|
+
fractal_server/tasks/v2/templates/1_create_venv.sh,sha256=PK0jdHKtQpda1zULebBaVPORt4t6V17wa4N1ohcj5ac,548
|
227
|
+
fractal_server/tasks/v2/templates/2_pip_install.sh,sha256=RDDfbFnGOK3aRuHyXqDOUNCGullzAr0zS7BFqG1CJeE,1720
|
228
|
+
fractal_server/tasks/v2/templates/3_pip_freeze.sh,sha256=JldREScEBI4cD_qjfX4UK7V4aI-FnX9ZvVNxgpSOBFc,168
|
229
|
+
fractal_server/tasks/v2/templates/4_pip_show.sh,sha256=84NGHlg6JIbrQktgGKyfGsggPFzy6RBJuOmIpPUhsrw,1747
|
230
|
+
fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh,sha256=q-6ZUvA6w6FDVEoSd9O63LaJ9tKZc7qAFH72SGPrd_k,284
|
231
|
+
fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh,sha256=n9C8w76YraLbeTe7NhuLzvAQiJCm_akL3Mc3EMfxrHo,1007
|
232
|
+
fractal_server/tasks/v2/utils_background.py,sha256=nAnel85xWIWPJjlv1XtRBJ59zoSjufvLobU8GlPRWUI,5155
|
233
|
+
fractal_server/tasks/v2/utils_database.py,sha256=6r56yyFPnEBrXl6ncmO6D76znzISQCFZqCYcD-Ummd4,1213
|
223
234
|
fractal_server/tasks/v2/utils_package_names.py,sha256=RDg__xrvQs4ieeVzmVdMcEh95vGQYrv9Hfal-5EDBM8,2393
|
224
235
|
fractal_server/tasks/v2/utils_python_interpreter.py,sha256=-EWh3Y3VqHLDOWUO_wG_wknqmGqKAD0O2KTLhNjrZaI,948
|
225
|
-
fractal_server/tasks/v2/utils_templates.py,sha256=
|
236
|
+
fractal_server/tasks/v2/utils_templates.py,sha256=C5WLuY3uGG2s53OEL-__H35-fmSlguwZx836BPFHBpE,2732
|
226
237
|
fractal_server/urls.py,sha256=5o_qq7PzKKbwq12NHSQZDmDitn5RAOeQ4xufu-2v9Zk,448
|
227
|
-
fractal_server/utils.py,sha256=
|
238
|
+
fractal_server/utils.py,sha256=utvmBx8K9I8hRWFquxna2pBaOqe0JifDL_NVPmihEJI,3525
|
228
239
|
fractal_server/zip_tools.py,sha256=xYpzBshysD2nmxkD5WLYqMzPYUcCRM3kYy-7n9bJL-U,4426
|
229
|
-
fractal_server-2.
|
230
|
-
fractal_server-2.
|
231
|
-
fractal_server-2.
|
232
|
-
fractal_server-2.
|
233
|
-
fractal_server-2.
|
240
|
+
fractal_server-2.9.0a0.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
|
241
|
+
fractal_server-2.9.0a0.dist-info/METADATA,sha256=6_oIEXxiAVbTAy9QvP753-1NvPXYmuWG-f-E4LMAQeo,4629
|
242
|
+
fractal_server-2.9.0a0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
243
|
+
fractal_server-2.9.0a0.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
|
244
|
+
fractal_server-2.9.0a0.dist-info/RECORD,,
|
@@ -1,22 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
from typing import Any
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
from sqlalchemy import Column
|
6
|
-
from sqlalchemy.types import DateTime
|
7
|
-
from sqlalchemy.types import JSON
|
8
|
-
from sqlmodel import Field
|
9
|
-
from sqlmodel import SQLModel
|
10
|
-
|
11
|
-
from ....utils import get_timestamp
|
12
|
-
|
13
|
-
|
14
|
-
class CollectionStateV2(SQLModel, table=True):
|
15
|
-
|
16
|
-
id: Optional[int] = Field(default=None, primary_key=True)
|
17
|
-
taskgroupv2_id: Optional[int] = Field(foreign_key="taskgroupv2.id")
|
18
|
-
data: dict[str, Any] = Field(sa_column=Column(JSON), default={})
|
19
|
-
timestamp: datetime = Field(
|
20
|
-
default_factory=get_timestamp,
|
21
|
-
sa_column=Column(DateTime(timezone=True)),
|
22
|
-
)
|