fractal-server 2.17.1__py3-none-any.whl → 2.17.1a0__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/__init__.py +1 -0
- fractal_server/app/models/security.py +14 -1
- fractal_server/app/models/user_settings.py +37 -0
- fractal_server/app/models/v2/project.py +4 -1
- fractal_server/app/models/v2/task_group.py +4 -1
- fractal_server/app/shutdown.py +19 -23
- fractal_server/config/_main.py +1 -1
- fractal_server/data_migrations/2_17_0.py +339 -0
- {fractal_server-2.17.1.dist-info → fractal_server-2.17.1a0.dist-info}/METADATA +1 -1
- {fractal_server-2.17.1.dist-info → fractal_server-2.17.1a0.dist-info}/RECORD +14 -16
- fractal_server/migrations/versions/45fbb391d7af_make_resource_id_fk_non_nullable.py +0 -46
- fractal_server/migrations/versions/49d0856e9569_drop_table.py +0 -63
- fractal_server/migrations/versions/7673fe18c05d_remove_project_dir_server_default.py +0 -29
- fractal_server/migrations/versions/caba9fb1ea5e_drop_useroauth_user_settings_id.py +0 -49
- {fractal_server-2.17.1.dist-info → fractal_server-2.17.1a0.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.1.dist-info → fractal_server-2.17.1a0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.1.dist-info → fractal_server-2.17.1a0.dist-info}/licenses/LICENSE +0 -0
fractal_server/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__VERSION__ = "2.17.
|
|
1
|
+
__VERSION__ = "2.17.1a0"
|
|
@@ -95,11 +95,24 @@ class UserOAuth(SQLModel, table=True):
|
|
|
95
95
|
ondelete="RESTRICT",
|
|
96
96
|
)
|
|
97
97
|
|
|
98
|
-
project_dir: str
|
|
98
|
+
# TODO-2.17.1: update to `project_dir: str`
|
|
99
|
+
project_dir: str = Field(
|
|
100
|
+
sa_column=Column(
|
|
101
|
+
String,
|
|
102
|
+
server_default="/PLACEHOLDER",
|
|
103
|
+
nullable=False,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
99
106
|
slurm_accounts: list[str] = Field(
|
|
100
107
|
sa_column=Column(ARRAY(String), server_default="{}"),
|
|
101
108
|
)
|
|
102
109
|
|
|
110
|
+
# TODO-2.17.1: remove
|
|
111
|
+
user_settings_id: int | None = Field(
|
|
112
|
+
foreign_key="user_settings.id",
|
|
113
|
+
default=None,
|
|
114
|
+
)
|
|
115
|
+
|
|
103
116
|
|
|
104
117
|
class UserGroup(SQLModel, table=True):
|
|
105
118
|
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from sqlalchemy import Column
|
|
2
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
3
|
+
from sqlmodel import Field
|
|
4
|
+
from sqlmodel import SQLModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# TODO-2.17.1: Drop `UserSettings`
|
|
8
|
+
class UserSettings(SQLModel, table=True):
|
|
9
|
+
"""
|
|
10
|
+
Comprehensive list of user settings.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
id: ID of database object
|
|
14
|
+
slurm_accounts:
|
|
15
|
+
List of SLURM accounts, to be used upon Fractal job submission.
|
|
16
|
+
ssh_host: SSH-reachable host where a SLURM client is available.
|
|
17
|
+
ssh_username: User on `ssh_host`.
|
|
18
|
+
ssh_private_key_path: Path of private SSH key for `ssh_username`.
|
|
19
|
+
slurm_user: Local user, to be impersonated via `sudo -u`
|
|
20
|
+
project_dir: Folder where `slurm_user` can write.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
__tablename__ = "user_settings"
|
|
24
|
+
|
|
25
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
26
|
+
slurm_accounts: list[str] = Field(
|
|
27
|
+
sa_column=Column(JSONB, server_default="[]", nullable=False)
|
|
28
|
+
)
|
|
29
|
+
ssh_host: str | None = None
|
|
30
|
+
ssh_username: str | None = None
|
|
31
|
+
ssh_private_key_path: str | None = None
|
|
32
|
+
|
|
33
|
+
slurm_user: str | None = None
|
|
34
|
+
project_dir: str | None = None
|
|
35
|
+
|
|
36
|
+
ssh_tasks_dir: str | None = None
|
|
37
|
+
ssh_jobs_dir: str | None = None
|
|
@@ -15,7 +15,10 @@ class ProjectV2(SQLModel, table=True):
|
|
|
15
15
|
id: int | None = Field(default=None, primary_key=True)
|
|
16
16
|
name: str
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# TODO-2.17.1: make `resource_id` not nullable
|
|
19
|
+
resource_id: int | None = Field(
|
|
20
|
+
foreign_key="resource.id", default=None, ondelete="RESTRICT"
|
|
21
|
+
)
|
|
19
22
|
timestamp_created: datetime = Field(
|
|
20
23
|
default_factory=get_timestamp,
|
|
21
24
|
sa_column=Column(DateTime(timezone=True), nullable=False),
|
|
@@ -42,7 +42,10 @@ class TaskGroupV2(SQLModel, table=True):
|
|
|
42
42
|
user_group_id: int | None = Field(
|
|
43
43
|
foreign_key="usergroup.id", default=None, ondelete="SET NULL"
|
|
44
44
|
)
|
|
45
|
-
|
|
45
|
+
# TODO-2.17.1: make `resource_id` not nullable
|
|
46
|
+
resource_id: int | None = Field(
|
|
47
|
+
foreign_key="resource.id", default=None, ondelete="RESTRICT"
|
|
48
|
+
)
|
|
46
49
|
|
|
47
50
|
origin: str
|
|
48
51
|
pkg_name: str
|
fractal_server/app/shutdown.py
CHANGED
|
@@ -12,48 +12,44 @@ from fractal_server.syringe import Inject
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
async def cleanup_after_shutdown(*, jobsV2: list[int], logger_name: str):
|
|
15
|
-
settings = Inject(get_settings)
|
|
16
15
|
logger = get_logger(logger_name)
|
|
17
16
|
logger.info("Cleanup function after shutdown")
|
|
18
|
-
|
|
17
|
+
stm_v2 = (
|
|
19
18
|
select(JobV2)
|
|
20
19
|
.where(JobV2.id.in_(jobsV2))
|
|
21
20
|
.where(JobV2.status == JobStatusTypeV2.SUBMITTED)
|
|
22
21
|
)
|
|
23
|
-
stm_ids = (
|
|
24
|
-
select(JobV2.id)
|
|
25
|
-
.where(JobV2.id.in_(jobsV2))
|
|
26
|
-
.where(JobV2.status == JobStatusTypeV2.SUBMITTED)
|
|
27
|
-
)
|
|
28
22
|
|
|
29
23
|
async for session in get_async_db():
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for job in
|
|
24
|
+
jobsV2_db = (await session.execute(stm_v2)).scalars().all()
|
|
25
|
+
|
|
26
|
+
for job in jobsV2_db:
|
|
33
27
|
_write_shutdown_file(job=job)
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
settings = Inject(get_settings)
|
|
30
|
+
|
|
37
31
|
t_start = time.perf_counter()
|
|
38
32
|
while (
|
|
39
33
|
time.perf_counter() - t_start
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
) < settings.FRACTAL_GRACEFUL_SHUTDOWN_TIME: # 30 seconds
|
|
35
|
+
logger.info("Waiting 3 seconds before checking")
|
|
36
|
+
time.sleep(3)
|
|
37
|
+
jobsV2_db = (await session.execute(stm_v2)).scalars().all()
|
|
38
|
+
|
|
39
|
+
if len(jobsV2_db) == 0:
|
|
40
|
+
logger.info(
|
|
41
|
+
"All jobs associated to this app are "
|
|
42
|
+
"either done or failed. Exit."
|
|
43
|
+
)
|
|
44
44
|
return
|
|
45
45
|
else:
|
|
46
|
-
logger.info(f"Some jobs are still 'submitted'
|
|
47
|
-
logger.info(f"Wait {interval:.4f} seconds before next check.")
|
|
48
|
-
time.sleep(interval)
|
|
46
|
+
logger.info(f"Some jobs are still 'submitted' {jobsV2_db=}")
|
|
49
47
|
logger.info(
|
|
50
48
|
"Graceful shutdown reached its maximum time, "
|
|
51
|
-
"but some jobs are still submitted
|
|
49
|
+
"but some jobs are still submitted"
|
|
52
50
|
)
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
jobs = (await session.execute(stm_objects)).scalars().all()
|
|
56
|
-
for job in jobs:
|
|
52
|
+
for job in jobsV2_db:
|
|
57
53
|
job.status = "failed"
|
|
58
54
|
job.log = (job.log or "") + "\nJob stopped due to app shutdown\n"
|
|
59
55
|
session.add(job)
|
fractal_server/config/_main.py
CHANGED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from dotenv.main import DotEnv
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from sqlalchemy.orm import Session
|
|
10
|
+
from sqlalchemy.sql.operators import is_
|
|
11
|
+
from sqlalchemy.sql.operators import is_not
|
|
12
|
+
from sqlmodel import select
|
|
13
|
+
|
|
14
|
+
from fractal_server.app.db import get_sync_db
|
|
15
|
+
from fractal_server.app.models import Profile
|
|
16
|
+
from fractal_server.app.models import ProjectV2
|
|
17
|
+
from fractal_server.app.models import Resource
|
|
18
|
+
from fractal_server.app.models import TaskGroupV2
|
|
19
|
+
from fractal_server.app.models import UserOAuth
|
|
20
|
+
from fractal_server.app.models import UserSettings
|
|
21
|
+
from fractal_server.app.schemas.v2.profile import cast_serialize_profile
|
|
22
|
+
from fractal_server.app.schemas.v2.resource import cast_serialize_resource
|
|
23
|
+
from fractal_server.config import get_settings
|
|
24
|
+
from fractal_server.runner.config import JobRunnerConfigLocal
|
|
25
|
+
from fractal_server.runner.config import JobRunnerConfigSLURM
|
|
26
|
+
from fractal_server.tasks.config import TasksPixiSettings
|
|
27
|
+
from fractal_server.tasks.config import TasksPythonSettings
|
|
28
|
+
from fractal_server.types import AbsolutePathStr
|
|
29
|
+
from fractal_server.types import ListUniqueNonEmptyString
|
|
30
|
+
from fractal_server.urls import normalize_url
|
|
31
|
+
|
|
32
|
+
logging.basicConfig(level=logging.INFO)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class UserUpdateInfo(BaseModel):
|
|
36
|
+
user_id: int
|
|
37
|
+
project_dir: AbsolutePathStr
|
|
38
|
+
slurm_accounts: ListUniqueNonEmptyString
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ProfileUsersUpdateInfo(BaseModel):
|
|
42
|
+
data: dict[str, Any]
|
|
43
|
+
user_updates: list[UserUpdateInfo]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_user_settings(user: UserOAuth, db: Session) -> UserSettings:
|
|
47
|
+
if user.user_settings_id is None:
|
|
48
|
+
sys.exit(f"User {user.email} is active but {user.user_settings_id=}.")
|
|
49
|
+
user_settings = db.get(UserSettings, user.user_settings_id)
|
|
50
|
+
return user_settings
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def assert_user_setting_key(
|
|
54
|
+
user: UserOAuth,
|
|
55
|
+
user_settings: UserSettings,
|
|
56
|
+
keys: list[str],
|
|
57
|
+
) -> None:
|
|
58
|
+
for key in keys:
|
|
59
|
+
if getattr(user_settings, key) is None:
|
|
60
|
+
sys.exit(
|
|
61
|
+
f"User {user.email} is active and verified but their "
|
|
62
|
+
f"user settings have {key}=None."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def prepare_profile_and_user_updates() -> dict[str, ProfileUsersUpdateInfo]:
|
|
67
|
+
settings = get_settings()
|
|
68
|
+
profiles_and_users: dict[str, ProfileUsersUpdateInfo] = {}
|
|
69
|
+
with next(get_sync_db()) as db:
|
|
70
|
+
# Get active&verified users
|
|
71
|
+
res = db.execute(
|
|
72
|
+
select(UserOAuth)
|
|
73
|
+
.where(is_(UserOAuth.is_active, True))
|
|
74
|
+
.where(is_(UserOAuth.is_verified, True))
|
|
75
|
+
.order_by(UserOAuth.id)
|
|
76
|
+
)
|
|
77
|
+
for user in res.unique().scalars().all():
|
|
78
|
+
# Get user settings
|
|
79
|
+
user_settings = _get_user_settings(user=user, db=db)
|
|
80
|
+
assert_user_setting_key(user, user_settings, ["project_dir"])
|
|
81
|
+
|
|
82
|
+
# Prepare profile data and user update
|
|
83
|
+
new_profile_data = dict()
|
|
84
|
+
if settings.FRACTAL_RUNNER_BACKEND == "local":
|
|
85
|
+
username = None
|
|
86
|
+
if settings.FRACTAL_RUNNER_BACKEND == "slurm_sudo":
|
|
87
|
+
assert_user_setting_key(user, user_settings, ["slurm_user"])
|
|
88
|
+
username = user_settings.slurm_user
|
|
89
|
+
elif settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
|
|
90
|
+
assert_user_setting_key(
|
|
91
|
+
user,
|
|
92
|
+
user_settings,
|
|
93
|
+
[
|
|
94
|
+
"ssh_username",
|
|
95
|
+
"ssh_private_key_path",
|
|
96
|
+
"ssh_tasks_dir",
|
|
97
|
+
"ssh_jobs_dir",
|
|
98
|
+
],
|
|
99
|
+
)
|
|
100
|
+
username = user_settings.ssh_username
|
|
101
|
+
new_profile_data.update(
|
|
102
|
+
ssh_key_path=user_settings.ssh_private_key_path,
|
|
103
|
+
tasks_remote_dir=normalize_url(
|
|
104
|
+
user_settings.ssh_tasks_dir
|
|
105
|
+
),
|
|
106
|
+
jobs_remote_dir=normalize_url(user_settings.ssh_jobs_dir),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
new_profile_data.update(
|
|
110
|
+
name=f"Profile {username}",
|
|
111
|
+
username=username,
|
|
112
|
+
resource_type=settings.FRACTAL_RUNNER_BACKEND,
|
|
113
|
+
)
|
|
114
|
+
cast_serialize_profile(new_profile_data)
|
|
115
|
+
|
|
116
|
+
user_update_info = UserUpdateInfo(
|
|
117
|
+
user_id=user.id,
|
|
118
|
+
project_dir=normalize_url(user_settings.project_dir),
|
|
119
|
+
slurm_accounts=user_settings.slurm_accounts or [],
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if username in profiles_and_users.keys():
|
|
123
|
+
if profiles_and_users[username].data != new_profile_data:
|
|
124
|
+
error_msg = (
|
|
125
|
+
"Profile data mismatch.\n"
|
|
126
|
+
f"{profiles_and_users[username].data=}\n"
|
|
127
|
+
f"{new_profile_data=}"
|
|
128
|
+
)
|
|
129
|
+
logging.error(error_msg)
|
|
130
|
+
sys.exit(error_msg)
|
|
131
|
+
profiles_and_users[username].user_updates.append(
|
|
132
|
+
user_update_info
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
profiles_and_users[username] = ProfileUsersUpdateInfo(
|
|
136
|
+
data=new_profile_data,
|
|
137
|
+
user_updates=[user_update_info],
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
return profiles_and_users
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_old_dotenv_variables() -> dict[str, str | None]:
|
|
144
|
+
"""
|
|
145
|
+
See
|
|
146
|
+
https://github.com/fractal-analytics-platform/fractal-server/blob/2.16.x/fractal_server/config.py
|
|
147
|
+
"""
|
|
148
|
+
OLD_DOTENV_FILE = ".fractal_server.env.old"
|
|
149
|
+
return dict(
|
|
150
|
+
**DotEnv(
|
|
151
|
+
dotenv_path=OLD_DOTENV_FILE,
|
|
152
|
+
override=False,
|
|
153
|
+
).dict()
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_TasksPythonSettings(
|
|
158
|
+
old_config: dict[str, str | None]
|
|
159
|
+
) -> dict[str, Any]:
|
|
160
|
+
versions = {}
|
|
161
|
+
for version_underscore in ["3_9", "3_10", "3_11", "3_12"]:
|
|
162
|
+
key = f"FRACTAL_TASKS_PYTHON_{version_underscore}"
|
|
163
|
+
version_dot = version_underscore.replace("_", ".")
|
|
164
|
+
value = old_config.get(key, None)
|
|
165
|
+
if value is not None:
|
|
166
|
+
versions[version_dot] = value
|
|
167
|
+
obj = TasksPythonSettings(
|
|
168
|
+
default_version=old_config["FRACTAL_TASKS_PYTHON_DEFAULT_VERSION"],
|
|
169
|
+
versions=versions,
|
|
170
|
+
pip_cache_dir=old_config.get("FRACTAL_PIP_CACHE_DIR", None),
|
|
171
|
+
)
|
|
172
|
+
return obj.model_dump()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def get_TasksPixiSettings(old_config: dict[str, str | None]) -> dict[str, Any]:
|
|
176
|
+
pixi_file = old_config.get("FRACTAL_PIXI_CONFIG_FILE", None)
|
|
177
|
+
if pixi_file is None:
|
|
178
|
+
return {}
|
|
179
|
+
with open(pixi_file) as f:
|
|
180
|
+
old_pixi_config = json.load(f)
|
|
181
|
+
TasksPixiSettings(**old_pixi_config)
|
|
182
|
+
return old_pixi_config
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get_JobRunnerConfigSLURM(
|
|
186
|
+
old_config: dict[str, str | None]
|
|
187
|
+
) -> dict[str, Any]:
|
|
188
|
+
slurm_file = old_config["FRACTAL_SLURM_CONFIG_FILE"]
|
|
189
|
+
with open(slurm_file) as f:
|
|
190
|
+
old_slurm_config = json.load(f)
|
|
191
|
+
JobRunnerConfigSLURM(**old_slurm_config)
|
|
192
|
+
return old_slurm_config
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_JobRunnerConfigLocal(
|
|
196
|
+
old_config: dict[str, str | None]
|
|
197
|
+
) -> dict[str, Any]:
|
|
198
|
+
local_file = old_config.get("FRACTAL_LOCAL_CONFIG_FILE", None)
|
|
199
|
+
if local_file is None or not Path(local_file).exists():
|
|
200
|
+
return JobRunnerConfigLocal().model_dump()
|
|
201
|
+
else:
|
|
202
|
+
with open(local_file) as f:
|
|
203
|
+
old_local_config = json.load(f)
|
|
204
|
+
JobRunnerConfigLocal(**old_local_config)
|
|
205
|
+
return old_local_config
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_ssh_host() -> str:
|
|
209
|
+
with next(get_sync_db()) as db:
|
|
210
|
+
res = db.execute(
|
|
211
|
+
select(UserSettings.ssh_host).where(
|
|
212
|
+
is_not(UserSettings.ssh_host, None)
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
hosts = res.scalars().all()
|
|
216
|
+
if len(set(hosts)) > 1:
|
|
217
|
+
host = max(set(hosts), key=hosts.count)
|
|
218
|
+
print(f"MOST FREQUENT HOST: {host}")
|
|
219
|
+
else:
|
|
220
|
+
host = hosts[0]
|
|
221
|
+
return host
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def prepare_resource_data(old_config: dict[str, str | None]) -> dict[str, Any]:
|
|
225
|
+
settings = get_settings()
|
|
226
|
+
|
|
227
|
+
resource_data = dict(
|
|
228
|
+
type=settings.FRACTAL_RUNNER_BACKEND,
|
|
229
|
+
name="Resource Name",
|
|
230
|
+
tasks_python_config=get_TasksPythonSettings(old_config),
|
|
231
|
+
tasks_pixi_config=get_TasksPixiSettings(old_config),
|
|
232
|
+
tasks_local_dir=old_config["FRACTAL_TASKS_DIR"],
|
|
233
|
+
jobs_local_dir=old_config["FRACTAL_RUNNER_WORKING_BASE_DIR"],
|
|
234
|
+
jobs_poll_interval=int(
|
|
235
|
+
old_config.get("FRACTAL_SLURM_POLL_INTERVAL", 15)
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
if settings.FRACTAL_RUNNER_BACKEND == "local":
|
|
239
|
+
resource_data["jobs_runner_config"] = get_JobRunnerConfigLocal(
|
|
240
|
+
old_config
|
|
241
|
+
)
|
|
242
|
+
elif settings.FRACTAL_RUNNER_BACKEND == "slurm_sudo":
|
|
243
|
+
resource_data["jobs_slurm_python_worker"] = old_config[
|
|
244
|
+
"FRACTAL_SLURM_WORKER_PYTHON"
|
|
245
|
+
]
|
|
246
|
+
resource_data["jobs_runner_config"] = get_JobRunnerConfigSLURM(
|
|
247
|
+
old_config
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
resource_data["jobs_slurm_python_worker"] = old_config[
|
|
251
|
+
"FRACTAL_SLURM_WORKER_PYTHON"
|
|
252
|
+
]
|
|
253
|
+
resource_data["jobs_runner_config"] = get_JobRunnerConfigSLURM(
|
|
254
|
+
old_config
|
|
255
|
+
)
|
|
256
|
+
resource_data["host"] = get_ssh_host()
|
|
257
|
+
|
|
258
|
+
resource_data = cast_serialize_resource(resource_data)
|
|
259
|
+
|
|
260
|
+
return resource_data
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def fix_db():
|
|
264
|
+
logging.info("START preliminary checks.")
|
|
265
|
+
|
|
266
|
+
# Read old env file
|
|
267
|
+
old_config = get_old_dotenv_variables()
|
|
268
|
+
|
|
269
|
+
# Prepare resource data
|
|
270
|
+
logging.info("START prepare_resource_data")
|
|
271
|
+
resource_data = prepare_resource_data(old_config)
|
|
272
|
+
logging.info("END prepare_resource_data")
|
|
273
|
+
|
|
274
|
+
# Prepare profile/users data
|
|
275
|
+
logging.info("START prepare_profile_and_user_updates")
|
|
276
|
+
profile_and_user_updates = prepare_profile_and_user_updates()
|
|
277
|
+
logging.info("END prepare_profile_and_user_updates")
|
|
278
|
+
|
|
279
|
+
logging.info("END preliminary checks.")
|
|
280
|
+
print()
|
|
281
|
+
|
|
282
|
+
with next(get_sync_db()) as db:
|
|
283
|
+
# Create new resource
|
|
284
|
+
resource = Resource(**resource_data)
|
|
285
|
+
db.add(resource)
|
|
286
|
+
db.commit()
|
|
287
|
+
db.refresh(resource)
|
|
288
|
+
db.expunge(resource)
|
|
289
|
+
resource_id = resource.id
|
|
290
|
+
logging.info(f"Created resource with {resource_id=}.")
|
|
291
|
+
|
|
292
|
+
# Update task groups
|
|
293
|
+
res = db.execute(select(TaskGroupV2).order_by(TaskGroupV2.id))
|
|
294
|
+
for taskgroup in res.scalars().all():
|
|
295
|
+
taskgroup.resource_id = resource_id
|
|
296
|
+
db.add(taskgroup)
|
|
297
|
+
db.commit()
|
|
298
|
+
logging.info(f"Set {resource_id=} foreign key for all task groups.")
|
|
299
|
+
|
|
300
|
+
# Update projects
|
|
301
|
+
res = db.execute(select(ProjectV2).order_by(ProjectV2.id))
|
|
302
|
+
for project in res.scalars().all():
|
|
303
|
+
project.resource_id = resource_id
|
|
304
|
+
db.add(project)
|
|
305
|
+
db.commit()
|
|
306
|
+
logging.info(f"Set {resource_id=} foreign key for all projects.")
|
|
307
|
+
print()
|
|
308
|
+
|
|
309
|
+
db.expunge_all()
|
|
310
|
+
|
|
311
|
+
for _, info in profile_and_user_updates.items():
|
|
312
|
+
# Create profile
|
|
313
|
+
profile_data = info.data
|
|
314
|
+
profile_data["resource_id"] = resource_id
|
|
315
|
+
profile = Profile(**profile_data)
|
|
316
|
+
db.add(profile)
|
|
317
|
+
db.commit()
|
|
318
|
+
db.refresh(profile)
|
|
319
|
+
db.expunge(profile)
|
|
320
|
+
profile_id = profile.id
|
|
321
|
+
logging.info(
|
|
322
|
+
f"Created profile '{profile.name}', with {profile.id=}."
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Update users
|
|
326
|
+
for user_update in info.user_updates:
|
|
327
|
+
user = db.get(UserOAuth, user_update.user_id)
|
|
328
|
+
user.profile_id = profile_id
|
|
329
|
+
user.project_dir = user_update.project_dir
|
|
330
|
+
user.slurm_accounts = user_update.slurm_accounts
|
|
331
|
+
db.add(user)
|
|
332
|
+
logging.info(f"Updated {user.email} with {user.project_dir=}.")
|
|
333
|
+
logging.info(
|
|
334
|
+
f"Associated {user.email} to profile {profile.name}."
|
|
335
|
+
)
|
|
336
|
+
print()
|
|
337
|
+
db.commit()
|
|
338
|
+
|
|
339
|
+
logging.info("END - all ok.")
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
fractal_server/__init__.py,sha256=
|
|
1
|
+
fractal_server/__init__.py,sha256=DW4yg1QyP_AsgqW1m4v28pnbhBE5KzkBv39LnIslJnE,25
|
|
2
2
|
fractal_server/__main__.py,sha256=qLbUicU1Ulaob_Eo5pspi-IH2xAkLfifJTH9gYEhZss,11427
|
|
3
3
|
fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
|
|
4
4
|
fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
fractal_server/app/db/__init__.py,sha256=sttX0mHVV0ESI1SJ1kcxUKiuEwqeP-BWsst0o_9Yo44,2810
|
|
6
|
-
fractal_server/app/models/__init__.py,sha256=
|
|
6
|
+
fractal_server/app/models/__init__.py,sha256=xJWiGAwpXmCpnFMC4c_HTqoUCzMOXrakoGLUH_uMvdA,415
|
|
7
7
|
fractal_server/app/models/linkusergroup.py,sha256=3KkkE4QIUAlTrBAZs_tVy0pGvAxUAq6yOEjflct_z2M,678
|
|
8
8
|
fractal_server/app/models/linkuserproject.py,sha256=hvaxh3Lkiy2uUCwB8gvn8RorCpvxSSdzWdCS_U1GL7g,315
|
|
9
|
-
fractal_server/app/models/security.py,sha256=
|
|
9
|
+
fractal_server/app/models/security.py,sha256=VThWDEmzUP4SgLsAvd5WjJ1p2WVxBuc6D5TMQJaOyd8,3873
|
|
10
|
+
fractal_server/app/models/user_settings.py,sha256=u0GOK1JdqDmXzA8hK2JV93rZxY_rF-0oKMkArRolnN8,1201
|
|
10
11
|
fractal_server/app/models/v2/__init__.py,sha256=A668GF4z_UPar6kAOwC-o_qUo3CIRJ3SmBGYTs3Xc7k,923
|
|
11
12
|
fractal_server/app/models/v2/accounting.py,sha256=i-2TsjqyuclxFQ21C-TeDoss7ZBTRuXdzIJfVr2UxwE,1081
|
|
12
13
|
fractal_server/app/models/v2/dataset.py,sha256=P_zy4dPQAqrCALQ6737VkAFk1SvcgYjnslGUZhPI8sc,1226
|
|
13
14
|
fractal_server/app/models/v2/history.py,sha256=CBN2WVg9vW5pHU1RP8TkB_nnJrwnuifCcxgnd53UtEE,2163
|
|
14
15
|
fractal_server/app/models/v2/job.py,sha256=YYzt3ef2CU1WXFNjlltR3ft2kM9T0Hq8oskSipQSxuM,2042
|
|
15
16
|
fractal_server/app/models/v2/profile.py,sha256=QqOE7XGeq-ckQAbGhcgzDN5zFFaTNrtcuWgOXy9psR8,440
|
|
16
|
-
fractal_server/app/models/v2/project.py,sha256=
|
|
17
|
+
fractal_server/app/models/v2/project.py,sha256=oXNcuNVDeNYZ60fwAx-Y_vnkS3xd9pwFdoT2pZmnBNI,918
|
|
17
18
|
fractal_server/app/models/v2/resource.py,sha256=ReaBGtKb3e0_1PZOZncdGqrttkrC-bsgDCv3wPCGfOs,3512
|
|
18
19
|
fractal_server/app/models/v2/task.py,sha256=iBIQB8POQE5MyKvLZhw7jZWlBhbrThzCDzRTcgiAczQ,1493
|
|
19
|
-
fractal_server/app/models/v2/task_group.py,sha256=
|
|
20
|
+
fractal_server/app/models/v2/task_group.py,sha256=gHkuyIBw8hkoMCwHgo08SWVMc8T1MXC5xqoW2YNd5Sw,4753
|
|
20
21
|
fractal_server/app/models/v2/workflow.py,sha256=gBjDXO-RytVT81aAlesImBhmVHrwNUrmsF_UsGa1qLM,1057
|
|
21
22
|
fractal_server/app/models/v2/workflowtask.py,sha256=qkTc-hcFLpJUVsEUbnDq2BJL0qg9jagy2doZeusF1ek,1266
|
|
22
23
|
fractal_server/app/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -93,14 +94,15 @@ fractal_server/app/schemas/v2/workflow.py,sha256=L-dW6SzCH_VNoH6ENip44lTgGGqVYHH
|
|
|
93
94
|
fractal_server/app/schemas/v2/workflowtask.py,sha256=6eweAMyziwaoMT-7R1fVJYunIeZKzT0-7fAVgPO_FEc,3639
|
|
94
95
|
fractal_server/app/security/__init__.py,sha256=k-La8Da89C1hSUGsiidrWo6Az4u6dbe5PzN1Ctt1t34,18394
|
|
95
96
|
fractal_server/app/security/signup_email.py,sha256=kphjq6TAygvPpYpg95QJWefyqmzdVrGz7fyRMctUJWE,1982
|
|
96
|
-
fractal_server/app/shutdown.py,sha256=
|
|
97
|
+
fractal_server/app/shutdown.py,sha256=ViSNJyXWU_iWPSDOOMGNh_iQdUFrdPh_jvf8vVKLpAo,1950
|
|
97
98
|
fractal_server/config/__init__.py,sha256=ZCmroNB50sUxJiFtkW0a4fFtmfyPnL4LWhtKY5FbQfg,737
|
|
98
99
|
fractal_server/config/_data.py,sha256=9Jyt83yrSsr_0_9ANWDAXz88_jjyFlcB5VWJGXq8aUY,2311
|
|
99
100
|
fractal_server/config/_database.py,sha256=k1z__MrslQjmel34yFvge0sroPUs1vBtT_OSlPY8pN8,1690
|
|
100
101
|
fractal_server/config/_email.py,sha256=j1QmZCyspNbD1xxkypc9Kv299tU3vTO1AqDFJ8-LZzQ,4201
|
|
101
|
-
fractal_server/config/_main.py,sha256=
|
|
102
|
+
fractal_server/config/_main.py,sha256=9v64gJsvY1oGP70_AoJMnyMIeRo7FcIg6T8NDV-p9as,1992
|
|
102
103
|
fractal_server/config/_oauth.py,sha256=7J4FphGVFfVmtQycCkas6scEJQJGZUGEzQ-t2PZiqSo,1934
|
|
103
104
|
fractal_server/config/_settings_config.py,sha256=tsyXQOnn9QKCFJD6hRo_dJXlQQyl70DbqgHMJoZ1xnY,144
|
|
105
|
+
fractal_server/data_migrations/2_17_0.py,sha256=DGxrQ5JSiJW5tzpfydCjYKjX7FsiF3y_k34b0mo_RaY,11757
|
|
104
106
|
fractal_server/data_migrations/README.md,sha256=_3AEFvDg9YkybDqCLlFPdDmGJvr6Tw7HRI14aZ3LOIw,398
|
|
105
107
|
fractal_server/data_migrations/tools.py,sha256=LeMeASwYGtEqd-3wOLle6WARdTGAimoyMmRbbJl-hAM,572
|
|
106
108
|
fractal_server/exceptions.py,sha256=7ftpWwNsTQmNonWCynhH5ErUh1haPPhIaVPrNHla7-o,53
|
|
@@ -120,9 +122,7 @@ fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py,sha
|
|
|
120
122
|
fractal_server/migrations/versions/1a83a5260664_rename.py,sha256=EkzTAjbJm7CfsLraIUbH9hkTj4M6XvmziVb4K9ZjKmQ,790
|
|
121
123
|
fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py,sha256=7OW3HmqAePHx53OWdEPzNxvtupxSR0lB_6tZF1b3JIM,1604
|
|
122
124
|
fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py,sha256=lANgTox0rz459_yo1Rw7fGCT1qw5sUCUXTLUMc_Bzf8,911
|
|
123
|
-
fractal_server/migrations/versions/45fbb391d7af_make_resource_id_fk_non_nullable.py,sha256=y9zr161YIWgnWbaMg1rahKN4b-vHjT3f5VSeoOAHaqI,1296
|
|
124
125
|
fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py,sha256=vePkVm1iUHiPNKLQ3KR7BBLdHruqBdl87j_tUCbMbEA,1414
|
|
125
|
-
fractal_server/migrations/versions/49d0856e9569_drop_table.py,sha256=qoq7cGUQmrnUj_wpV2mRqVneyoKqglgbrgzW_8eS_5w,1835
|
|
126
126
|
fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py,sha256=-wHe-fOffmYeAm0JXVl_lxZ7hhDkaEVqxgxpHkb_uL8,954
|
|
127
127
|
fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py,sha256=Mob8McGYAcmgvrseyyYOa54E6Gsgr-4SiGdC-r9O4_A,1157
|
|
128
128
|
fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py,sha256=JOrVa6mGzqZ6H61YCFVOed64vFRjTWGWyN3z7NE3T08,3270
|
|
@@ -130,7 +130,6 @@ fractal_server/migrations/versions/50a13d6138fd_initial_schema.py,sha256=zwXegXs
|
|
|
130
130
|
fractal_server/migrations/versions/5bf02391cfef_v2.py,sha256=axhNkr_H6R4rRbY7oGYazNbFvPXeSyBDWFVbKNmiqs8,8433
|
|
131
131
|
fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py,sha256=Q-DsMzG3IcUV2Ol1dhJWosDvKERamBE6QvA2zzS5zpQ,1632
|
|
132
132
|
fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py,sha256=mbWuCkTpRAdGbRhW7lhXs_e5S6O37UAcCN6JfoY5H8A,1353
|
|
133
|
-
fractal_server/migrations/versions/7673fe18c05d_remove_project_dir_server_default.py,sha256=LAC1Uv4SeLkqjXPyqj5Mof8L0105gxqS1TYKzNVX4GE,795
|
|
134
133
|
fractal_server/migrations/versions/791ce783d3d8_add_indices.py,sha256=gNE6AgJgeJZY99Fbd336Z9see3gRMQvuNBC0xDk_5sw,1154
|
|
135
134
|
fractal_server/migrations/versions/83bc2ad3ffcc_2_17_0.py,sha256=U7t_8n58taRkd9sxCXOshrTr9M5AhlsQne8SGKa5Jt4,6377
|
|
136
135
|
fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py,sha256=NSCuhANChsg76vBkShBl-9tQ4VEHubOjtAv1etHhlvY,2684
|
|
@@ -150,7 +149,6 @@ fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py,sha25
|
|
|
150
149
|
fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py,sha256=loDrqBB-9U3vqLKePEeJy4gK4EuPs_1F345mdrnoCt0,1293
|
|
151
150
|
fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py,sha256=Q01lPlBNQgi3hpoUquWj2QUEF7cTsyQ7uikUhWunzWY,10035
|
|
152
151
|
fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py,sha256=Y1cPwmFOZ4mx3v2XZM6adgu8u0L0VD_R4ADURyMb2ro,1102
|
|
153
|
-
fractal_server/migrations/versions/caba9fb1ea5e_drop_useroauth_user_settings_id.py,sha256=8tlWVmux-c-fB9hMO4JEsaPMXRwLN_X3PpC0rUuFrYw,1320
|
|
154
152
|
fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py,sha256=HN3_Pk8G81SzdYjg4K1RZAyjKSlsZGvcYE2nWOUbwxQ,3861
|
|
155
153
|
fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py,sha256=6cHEZFuTXiQg9yu32Y3RH1XAl71av141WQ6UMbiITIg,949
|
|
156
154
|
fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py,sha256=yGWSA2HIHUybcVy66xBITk08opV2DFYSCIIrulaUZhI,901
|
|
@@ -257,8 +255,8 @@ fractal_server/types/validators/_workflow_task_arguments_validators.py,sha256=HL
|
|
|
257
255
|
fractal_server/urls.py,sha256=QjIKAC1a46bCdiPMu3AlpgFbcv6a4l3ABcd5xz190Og,471
|
|
258
256
|
fractal_server/utils.py,sha256=SYVVUuXe_nWyrJLsy7QA-KJscwc5PHEXjvsW4TK7XQI,2180
|
|
259
257
|
fractal_server/zip_tools.py,sha256=H0w7wS5yE4ebj7hw1_77YQ959dl2c-L0WX6J_ro1TY4,4884
|
|
260
|
-
fractal_server-2.17.
|
|
261
|
-
fractal_server-2.17.
|
|
262
|
-
fractal_server-2.17.
|
|
263
|
-
fractal_server-2.17.
|
|
264
|
-
fractal_server-2.17.
|
|
258
|
+
fractal_server-2.17.1a0.dist-info/METADATA,sha256=XJQY23pLEp0LL8hg0EN9kPh7P6wfJLD9GwcHq8psXQo,4226
|
|
259
|
+
fractal_server-2.17.1a0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
260
|
+
fractal_server-2.17.1a0.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
|
|
261
|
+
fractal_server-2.17.1a0.dist-info/licenses/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
|
|
262
|
+
fractal_server-2.17.1a0.dist-info/RECORD,,
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"""Make resource_id FK non-nullable
|
|
2
|
-
|
|
3
|
-
Revision ID: 45fbb391d7af
|
|
4
|
-
Revises: caba9fb1ea5e
|
|
5
|
-
Create Date: 2025-11-11 16:39:12.813766
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
import sqlalchemy as sa
|
|
9
|
-
from alembic import op
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# revision identifiers, used by Alembic.
|
|
13
|
-
revision = "45fbb391d7af"
|
|
14
|
-
down_revision = "caba9fb1ea5e"
|
|
15
|
-
branch_labels = None
|
|
16
|
-
depends_on = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def upgrade() -> None:
|
|
20
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
-
with op.batch_alter_table("projectv2", schema=None) as batch_op:
|
|
22
|
-
batch_op.alter_column(
|
|
23
|
-
"resource_id", existing_type=sa.INTEGER(), nullable=False
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
with op.batch_alter_table("taskgroupv2", schema=None) as batch_op:
|
|
27
|
-
batch_op.alter_column(
|
|
28
|
-
"resource_id", existing_type=sa.INTEGER(), nullable=False
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
# ### end Alembic commands ###
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def downgrade() -> None:
|
|
35
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
36
|
-
with op.batch_alter_table("taskgroupv2", schema=None) as batch_op:
|
|
37
|
-
batch_op.alter_column(
|
|
38
|
-
"resource_id", existing_type=sa.INTEGER(), nullable=True
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
with op.batch_alter_table("projectv2", schema=None) as batch_op:
|
|
42
|
-
batch_op.alter_column(
|
|
43
|
-
"resource_id", existing_type=sa.INTEGER(), nullable=True
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
# ### end Alembic commands ###
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""Drop table
|
|
2
|
-
|
|
3
|
-
Revision ID: 49d0856e9569
|
|
4
|
-
Revises: 45fbb391d7af
|
|
5
|
-
Create Date: 2025-11-11 16:39:41.497832
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
import sqlalchemy as sa
|
|
9
|
-
from alembic import op
|
|
10
|
-
from sqlalchemy.dialects import postgresql
|
|
11
|
-
|
|
12
|
-
# revision identifiers, used by Alembic.
|
|
13
|
-
revision = "49d0856e9569"
|
|
14
|
-
down_revision = "45fbb391d7af"
|
|
15
|
-
branch_labels = None
|
|
16
|
-
depends_on = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def upgrade() -> None:
|
|
20
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
-
op.drop_table("user_settings")
|
|
22
|
-
# ### end Alembic commands ###
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def downgrade() -> None:
|
|
26
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
27
|
-
op.create_table(
|
|
28
|
-
"user_settings",
|
|
29
|
-
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
|
|
30
|
-
sa.Column(
|
|
31
|
-
"slurm_accounts",
|
|
32
|
-
postgresql.JSONB(astext_type=sa.Text()),
|
|
33
|
-
server_default=sa.text("'[]'::json"),
|
|
34
|
-
autoincrement=False,
|
|
35
|
-
nullable=False,
|
|
36
|
-
),
|
|
37
|
-
sa.Column(
|
|
38
|
-
"ssh_host", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
39
|
-
),
|
|
40
|
-
sa.Column(
|
|
41
|
-
"ssh_username", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
42
|
-
),
|
|
43
|
-
sa.Column(
|
|
44
|
-
"ssh_private_key_path",
|
|
45
|
-
sa.VARCHAR(),
|
|
46
|
-
autoincrement=False,
|
|
47
|
-
nullable=True,
|
|
48
|
-
),
|
|
49
|
-
sa.Column(
|
|
50
|
-
"ssh_tasks_dir", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
51
|
-
),
|
|
52
|
-
sa.Column(
|
|
53
|
-
"ssh_jobs_dir", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
54
|
-
),
|
|
55
|
-
sa.Column(
|
|
56
|
-
"slurm_user", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
57
|
-
),
|
|
58
|
-
sa.Column(
|
|
59
|
-
"project_dir", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
60
|
-
),
|
|
61
|
-
sa.PrimaryKeyConstraint("id", name=op.f("pk_user_settings")),
|
|
62
|
-
)
|
|
63
|
-
# ### end Alembic commands ###
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"""Remove project_dir server_default
|
|
2
|
-
|
|
3
|
-
Revision ID: 7673fe18c05d
|
|
4
|
-
Revises: 49d0856e9569
|
|
5
|
-
Create Date: 2025-11-11 16:50:20.079193
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
from alembic import op
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# revision identifiers, used by Alembic.
|
|
12
|
-
revision = "7673fe18c05d"
|
|
13
|
-
down_revision = "49d0856e9569"
|
|
14
|
-
branch_labels = None
|
|
15
|
-
depends_on = None
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def upgrade() -> None:
|
|
19
|
-
"""
|
|
20
|
-
Remove `server_default` for `project_dir` column - see
|
|
21
|
-
https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.alter_column.params.server_default
|
|
22
|
-
"""
|
|
23
|
-
with op.batch_alter_table("user_oauth") as batch_op:
|
|
24
|
-
batch_op.alter_column("project_dir", server_default=None)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def downgrade() -> None:
|
|
28
|
-
with op.batch_alter_table("user_oauth") as batch_op:
|
|
29
|
-
batch_op.alter_column("project_dir", server_default="/PLACEHOLDER")
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""Drop UserOAuth.user_settings_id
|
|
2
|
-
|
|
3
|
-
Revision ID: caba9fb1ea5e
|
|
4
|
-
Revises: 83bc2ad3ffcc
|
|
5
|
-
Create Date: 2025-11-11 16:38:27.243693
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
import sqlalchemy as sa
|
|
9
|
-
from alembic import op
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# revision identifiers, used by Alembic.
|
|
13
|
-
revision = "caba9fb1ea5e"
|
|
14
|
-
down_revision = "83bc2ad3ffcc"
|
|
15
|
-
branch_labels = None
|
|
16
|
-
depends_on = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def upgrade() -> None:
|
|
20
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
-
with op.batch_alter_table("user_oauth", schema=None) as batch_op:
|
|
22
|
-
batch_op.drop_constraint(
|
|
23
|
-
batch_op.f("fk_user_oauth_user_settings_id_user_settings"),
|
|
24
|
-
type_="foreignkey",
|
|
25
|
-
)
|
|
26
|
-
batch_op.drop_column("user_settings_id")
|
|
27
|
-
|
|
28
|
-
# ### end Alembic commands ###
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def downgrade() -> None:
|
|
32
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
33
|
-
with op.batch_alter_table("user_oauth", schema=None) as batch_op:
|
|
34
|
-
batch_op.add_column(
|
|
35
|
-
sa.Column(
|
|
36
|
-
"user_settings_id",
|
|
37
|
-
sa.INTEGER(),
|
|
38
|
-
autoincrement=False,
|
|
39
|
-
nullable=True,
|
|
40
|
-
)
|
|
41
|
-
)
|
|
42
|
-
batch_op.create_foreign_key(
|
|
43
|
-
batch_op.f("fk_user_oauth_user_settings_id_user_settings"),
|
|
44
|
-
"user_settings",
|
|
45
|
-
["user_settings_id"],
|
|
46
|
-
["id"],
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
# ### end Alembic commands ###
|
|
File without changes
|
|
File without changes
|
|
File without changes
|