fractal-server 2.17.0a5__py3-none-any.whl → 2.17.0a7__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 (41) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/security.py +7 -1
  3. fractal_server/app/models/user_settings.py +4 -0
  4. fractal_server/app/models/v2/profile.py +1 -1
  5. fractal_server/app/models/v2/project.py +3 -1
  6. fractal_server/app/models/v2/resource.py +2 -2
  7. fractal_server/app/models/v2/task_group.py +1 -1
  8. fractal_server/app/routes/admin/v2/profile.py +14 -0
  9. fractal_server/app/routes/admin/v2/resource.py +9 -52
  10. fractal_server/app/routes/admin/v2/task.py +9 -0
  11. fractal_server/app/routes/admin/v2/task_group.py +11 -7
  12. fractal_server/app/routes/api/__init__.py +9 -0
  13. fractal_server/app/routes/api/v2/_aux_functions.py +6 -16
  14. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +35 -7
  15. fractal_server/app/routes/api/v2/project.py +8 -4
  16. fractal_server/app/routes/api/v2/submit.py +4 -3
  17. fractal_server/app/routes/api/v2/task.py +18 -5
  18. fractal_server/app/routes/api/v2/task_collection.py +3 -5
  19. fractal_server/app/routes/api/v2/task_collection_custom.py +2 -5
  20. fractal_server/app/routes/api/v2/task_collection_pixi.py +2 -5
  21. fractal_server/app/routes/auth/current_user.py +9 -14
  22. fractal_server/app/routes/auth/oauth.py +16 -6
  23. fractal_server/app/routes/auth/users.py +1 -2
  24. fractal_server/app/schemas/user.py +3 -3
  25. fractal_server/app/schemas/v2/__init__.py +1 -0
  26. fractal_server/app/schemas/v2/task_group.py +4 -0
  27. fractal_server/config/__init__.py +6 -1
  28. fractal_server/config/_data.py +68 -0
  29. fractal_server/config/_main.py +1 -65
  30. fractal_server/config/_oauth.py +2 -2
  31. fractal_server/main.py +3 -2
  32. fractal_server/migrations/naming_convention.py +1 -1
  33. fractal_server/migrations/versions/{a80ac5a352bf_resource_profile.py → 83bc2ad3ffcc_2_17_0.py} +31 -31
  34. {fractal_server-2.17.0a5.dist-info → fractal_server-2.17.0a7.dist-info}/METADATA +4 -5
  35. {fractal_server-2.17.0a5.dist-info → fractal_server-2.17.0a7.dist-info}/RECORD +38 -40
  36. fractal_server/data_migrations/2_14_10.py +0 -48
  37. fractal_server/migrations/versions/90f6508c6379_drop_useroauth_username.py +0 -36
  38. fractal_server/migrations/versions/f65ee53991e3_user_settings_related.py +0 -67
  39. {fractal_server-2.17.0a5.dist-info → fractal_server-2.17.0a7.dist-info}/WHEEL +0 -0
  40. {fractal_server-2.17.0a5.dist-info → fractal_server-2.17.0a7.dist-info}/entry_points.txt +0 -0
  41. {fractal_server-2.17.0a5.dist-info → fractal_server-2.17.0a7.dist-info}/licenses/LICENSE +0 -0
@@ -10,7 +10,6 @@ from fastapi import Response
10
10
  from fastapi import status
11
11
  from fastapi import UploadFile
12
12
 
13
- from ._aux_functions import _get_resource_and_profile_ids
14
13
  from fractal_server.app.db import AsyncSession
15
14
  from fractal_server.app.db import get_async_db
16
15
  from fractal_server.app.models import UserOAuth
@@ -90,6 +89,7 @@ async def collect_task_pixi(
90
89
  ) -> TaskGroupActivityV2Read:
91
90
  # Get validated resource and profile
92
91
  resource, profile = await validate_user_profile(user=user, db=db)
92
+ resource_id = resource.id
93
93
 
94
94
  # Check if Pixi is available
95
95
  if not resource.tasks_pixi_config:
@@ -133,10 +133,6 @@ async def collect_task_pixi(
133
133
  Path(base_tasks_path) / str(user.id) / pkg_name / version
134
134
  ).as_posix()
135
135
 
136
- resource_id, _ = await _get_resource_and_profile_ids(
137
- user_id=user.id, db=db
138
- )
139
-
140
136
  task_group_attrs = dict(
141
137
  user_id=user.id,
142
138
  user_group_id=user_group_id,
@@ -152,6 +148,7 @@ async def collect_task_pixi(
152
148
  user_id=user.id,
153
149
  pkg_name=task_group_attrs["pkg_name"],
154
150
  version=task_group_attrs["version"],
151
+ user_resource_id=resource_id,
155
152
  db=db,
156
153
  )
157
154
  await _verify_non_duplication_group_constraint(
@@ -5,7 +5,6 @@ import os
5
5
 
6
6
  from fastapi import APIRouter
7
7
  from fastapi import Depends
8
- from fastapi_users import schemas
9
8
  from sqlalchemy.ext.asyncio import AsyncSession
10
9
  from sqlmodel import select
11
10
 
@@ -25,8 +24,8 @@ from fractal_server.app.schemas.user import UserUpdate
25
24
  from fractal_server.app.schemas.user import UserUpdateStrict
26
25
  from fractal_server.app.security import get_user_manager
27
26
  from fractal_server.app.security import UserManager
28
- from fractal_server.config import get_settings
29
- from fractal_server.config import ViewerAuthScheme
27
+ from fractal_server.config import DataAuthScheme
28
+ from fractal_server.config import get_data_settings
30
29
  from fractal_server.syringe import Inject
31
30
 
32
31
  router_current_user = APIRouter()
@@ -66,7 +65,7 @@ async def patch_current_user(
66
65
  # their own password
67
66
 
68
67
  user = await user_manager.update(update, current_user, safe=True)
69
- validated_user = schemas.model_validate(UserOAuth, user.model_dump())
68
+ validated_user = UserOAuth.model_validate(user.model_dump())
70
69
 
71
70
  patched_user = await db.get(
72
71
  UserOAuth, validated_user.id, populate_existing=True
@@ -117,14 +116,14 @@ async def get_current_user_allowed_viewer_paths(
117
116
  ) -> list[str]:
118
117
  """
119
118
  Returns the allowed viewer paths for current user, according to the
120
- selected FRACTAL_VIEWER_AUTHORIZATION_SCHEME
119
+ selected FRACTAL_DATA_AUTH_SCHEME
121
120
  """
122
121
 
123
- settings = Inject(get_settings)
122
+ data_settings = Inject(get_data_settings)
124
123
 
125
124
  authorized_paths = []
126
125
 
127
- if settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME == ViewerAuthScheme.NONE:
126
+ if data_settings.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.NONE:
128
127
  return authorized_paths
129
128
 
130
129
  # Append `project_dir` to the list of authorized paths
@@ -133,20 +132,16 @@ async def get_current_user_allowed_viewer_paths(
133
132
  # If auth scheme is "users-folders" and `slurm_user` is set,
134
133
  # build and append the user folder
135
134
  if (
136
- settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME
137
- == ViewerAuthScheme.USERS_FOLDERS
135
+ data_settings.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.USERS_FOLDERS
138
136
  and current_user.profile_id is not None
139
137
  ):
140
138
  profile = await db.get(Profile, current_user.profile_id)
141
139
  if profile is not None and profile.username is not None:
142
- base_folder = settings.FRACTAL_VIEWER_BASE_FOLDER
140
+ base_folder = data_settings.FRACTAL_DATA_BASE_FOLDER
143
141
  user_folder = os.path.join(base_folder, profile.username)
144
142
  authorized_paths.append(user_folder)
145
143
 
146
- if (
147
- settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME
148
- == ViewerAuthScheme.VIEWER_PATHS
149
- ):
144
+ if data_settings.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.VIEWER_PATHS:
150
145
  # Returns the union of `viewer_paths` for all user's groups
151
146
  cmd = (
152
147
  select(UserGroup.viewer_paths)
@@ -2,6 +2,7 @@ from fastapi import APIRouter
2
2
  from httpx_oauth.clients.github import GitHubOAuth2
3
3
  from httpx_oauth.clients.google import GoogleOAuth2
4
4
  from httpx_oauth.clients.openid import OpenID
5
+ from httpx_oauth.clients.openid import OpenIDConfigurationError
5
6
 
6
7
  from . import cookie_backend
7
8
  from . import fastapi_users
@@ -26,11 +27,21 @@ def _create_client_google(cfg: OAuthSettings) -> GoogleOAuth2:
26
27
 
27
28
 
28
29
  def _create_client_oidc(cfg: OAuthSettings) -> OpenID:
29
- return OpenID(
30
- client_id=cfg.OAUTH_CLIENT_ID.get_secret_value(),
31
- client_secret=cfg.OAUTH_CLIENT_SECRET.get_secret_value(),
32
- openid_configuration_endpoint=cfg.OAUTH_OIDC_CONFIG_ENDPOINT,
33
- )
30
+ try:
31
+ open_id = OpenID(
32
+ client_id=cfg.OAUTH_CLIENT_ID.get_secret_value(),
33
+ client_secret=cfg.OAUTH_CLIENT_SECRET.get_secret_value(),
34
+ openid_configuration_endpoint=cfg.OAUTH_OIDC_CONFIG_ENDPOINT.get_secret_value(), # noqa
35
+ )
36
+ except OpenIDConfigurationError as e:
37
+ OAUTH_OIDC_CONFIG_ENDPOINT = (
38
+ cfg.OAUTH_OIDC_CONFIG_ENDPOINT.get_secret_value()
39
+ )
40
+ raise RuntimeError(
41
+ f"Cannot initialize OpenID client. Original error: '{e}'. "
42
+ f"Hint: is {OAUTH_OIDC_CONFIG_ENDPOINT=} reachable?"
43
+ )
44
+ return open_id
34
45
 
35
46
 
36
47
  def get_oauth_router() -> APIRouter | None:
@@ -44,7 +55,6 @@ def get_oauth_router() -> APIRouter | None:
44
55
  return None
45
56
 
46
57
  client_name = oauth_settings.OAUTH_CLIENT_NAME
47
-
48
58
  if client_name == "google":
49
59
  client = _create_client_google(oauth_settings)
50
60
  elif client_name == "github":
@@ -6,7 +6,6 @@ from fastapi import Depends
6
6
  from fastapi import HTTPException
7
7
  from fastapi import status
8
8
  from fastapi_users import exceptions
9
- from fastapi_users import schemas
10
9
  from fastapi_users.router.common import ErrorCode
11
10
  from sqlalchemy.ext.asyncio import AsyncSession
12
11
  from sqlmodel import func
@@ -80,7 +79,7 @@ async def patch_user(
80
79
  safe=False,
81
80
  request=None,
82
81
  )
83
- validated_user = schemas.model_validate(UserOAuth, user.model_dump())
82
+ validated_user = UserOAuth.model_validate(user.model_dump())
84
83
  patched_user = await db.get(
85
84
  UserOAuth, validated_user.id, populate_existing=True
86
85
  )
@@ -76,8 +76,8 @@ class UserUpdate(schemas.BaseUserUpdate):
76
76
  profile_id: int | None = None
77
77
  project_dir: Annotated[
78
78
  AbsolutePathStr, AfterValidator(_validate_cmd)
79
- ] | None = None
80
- slurm_accounts: ListUniqueNonEmptyString | None = None
79
+ ] = None
80
+ slurm_accounts: ListUniqueNonEmptyString = None
81
81
 
82
82
 
83
83
  class UserUpdateStrict(BaseModel):
@@ -89,7 +89,7 @@ class UserUpdateStrict(BaseModel):
89
89
  """
90
90
 
91
91
  model_config = ConfigDict(extra="forbid")
92
- slurm_accounts: ListUniqueNonEmptyString | None = None
92
+ slurm_accounts: ListUniqueNonEmptyString = None
93
93
 
94
94
 
95
95
  class UserCreate(schemas.BaseUserCreate):
@@ -52,6 +52,7 @@ from .task_group import TaskGroupActivityStatusV2 # noqa F401
52
52
  from .task_group import TaskGroupActivityV2Read # noqa F401
53
53
  from .task_group import TaskGroupCreateV2 # noqa F401
54
54
  from .task_group import TaskGroupCreateV2Strict # noqa F401
55
+ from .task_group import TaskGroupReadSuperuser # noqa F401
55
56
  from .task_group import TaskGroupReadV2 # noqa F401
56
57
  from .task_group import TaskGroupUpdateV2 # noqa F401
57
58
  from .task_group import TaskGroupV2OriginEnum # noqa F401
@@ -96,6 +96,10 @@ class TaskGroupReadV2(BaseModel):
96
96
  return v.isoformat()
97
97
 
98
98
 
99
+ class TaskGroupReadSuperuser(TaskGroupReadV2):
100
+ resource_id: int
101
+
102
+
99
103
  class TaskGroupUpdateV2(BaseModel):
100
104
  model_config = ConfigDict(extra="forbid")
101
105
  user_group_id: int | None = None
@@ -1,8 +1,9 @@
1
+ from ._data import DataAuthScheme # noqa F401
2
+ from ._data import DataSettings
1
3
  from ._database import DatabaseSettings
2
4
  from ._email import EmailSettings
3
5
  from ._email import PublicEmailSettings # noqa F401
4
6
  from ._main import Settings
5
- from ._main import ViewerAuthScheme # noqa F401
6
7
  from ._oauth import OAuthSettings
7
8
 
8
9
 
@@ -20,3 +21,7 @@ def get_email_settings(email_settings=EmailSettings()) -> EmailSettings:
20
21
 
21
22
  def get_oauth_settings(oauth_settings=OAuthSettings()) -> OAuthSettings:
22
23
  return oauth_settings
24
+
25
+
26
+ def get_data_settings(data_settings=DataSettings()) -> DataSettings:
27
+ return data_settings
@@ -0,0 +1,68 @@
1
+ from enum import StrEnum
2
+ from typing import Self
3
+
4
+ from pydantic import model_validator
5
+ from pydantic_settings import BaseSettings
6
+ from pydantic_settings import SettingsConfigDict
7
+
8
+ from ._settings_config import SETTINGS_CONFIG_DICT
9
+ from fractal_server.types import AbsolutePathStr
10
+
11
+
12
+ class DataAuthScheme(StrEnum):
13
+ VIEWER_PATHS = "viewer-paths"
14
+ USERS_FOLDERS = "users-folders"
15
+ NONE = "none"
16
+
17
+
18
+ class DataSettings(BaseSettings):
19
+ """
20
+ Settings for the `fractal-data` integration.
21
+ """
22
+
23
+ model_config = SettingsConfigDict(**SETTINGS_CONFIG_DICT)
24
+
25
+ FRACTAL_DATA_AUTH_SCHEME: DataAuthScheme = "none"
26
+ """
27
+ Defines how the list of allowed viewer paths is built.
28
+
29
+ This variable affects the `GET /auth/current-user/allowed-viewer-paths/`
30
+ response, which is then consumed by
31
+ [fractal-data](https://github.com/fractal-analytics-platform/fractal-data).
32
+
33
+ Options:
34
+
35
+ - "viewer-paths": The list of allowed viewer paths will include the user's
36
+ `project_dir` along with any path defined in user groups' `viewer_paths`
37
+ attributes.
38
+ - "users-folders": The list will consist of the user's `project_dir` and a
39
+ user-specific folder. The user folder is constructed by concatenating
40
+ the base folder `FRACTAL_DATA_BASE_FOLDER` with the user's profile
41
+ `username`.
42
+ - "none": An empty list will be returned, indicating no access to
43
+ viewer paths. Useful when vizarr viewer is not used.
44
+ """
45
+
46
+ FRACTAL_DATA_BASE_FOLDER: AbsolutePathStr | None = None
47
+ """
48
+ Base path to Zarr files that will be served by fractal-vizarr-viewer;
49
+ This variable is required and used only when
50
+ FRACTAL_DATA_AUTHORIZATION_SCHEME is set to "users-folders".
51
+ """
52
+
53
+ @model_validator(mode="after")
54
+ def check(self: Self) -> Self:
55
+ """
56
+ `FRACTAL_DATA_BASE_FOLDER` is required when
57
+ `FRACTAL_DATA_AUTHORIZATION_SCHEME` is set to `"users-folders"`.
58
+ """
59
+ if (
60
+ self.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.USERS_FOLDERS
61
+ and self.FRACTAL_DATA_BASE_FOLDER is None
62
+ ):
63
+ raise ValueError(
64
+ "FRACTAL_DATA_BASE_FOLDER is required when "
65
+ "FRACTAL_DATA_AUTH_SCHEME is set to "
66
+ "users-folders"
67
+ )
68
+ return self
@@ -1,7 +1,5 @@
1
1
  import logging
2
- from enum import StrEnum
3
2
  from typing import Literal
4
- from typing import TypeVar
5
3
 
6
4
  from pydantic import HttpUrl
7
5
  from pydantic import SecretStr
@@ -9,20 +7,6 @@ from pydantic_settings import BaseSettings
9
7
  from pydantic_settings import SettingsConfigDict
10
8
 
11
9
  from ._settings_config import SETTINGS_CONFIG_DICT
12
- from fractal_server.types import AbsolutePathStr
13
-
14
-
15
- class FractalConfigurationError(ValueError):
16
- pass
17
-
18
-
19
- class ViewerAuthScheme(StrEnum):
20
- VIEWER_PATHS = "viewer-paths"
21
- USERS_FOLDERS = "users-folders"
22
- NONE = "none"
23
-
24
-
25
- T = TypeVar("T")
26
10
 
27
11
 
28
12
  class Settings(BaseSettings):
@@ -34,7 +18,6 @@ class Settings(BaseSettings):
34
18
 
35
19
  model_config = SettingsConfigDict(**SETTINGS_CONFIG_DICT)
36
20
 
37
- # JWT TOKEN
38
21
  JWT_EXPIRE_SECONDS: int = 180
39
22
  """
40
23
  JWT token lifetime, in seconds.
@@ -48,7 +31,6 @@ class Settings(BaseSettings):
48
31
  it.
49
32
  """
50
33
 
51
- # COOKIE TOKEN
52
34
  COOKIE_EXPIRE_SECONDS: int = 86400
53
35
  """
54
36
  Cookie token lifetime, in seconds.
@@ -69,7 +51,7 @@ class Settings(BaseSettings):
69
51
  Only logs of with this level (or higher) will appear in the console logs.
70
52
  """
71
53
 
72
- FRACTAL_API_MAX_JOB_LIST_LENGTH: int = 50
54
+ FRACTAL_API_MAX_JOB_LIST_LENGTH: int = 25
73
55
  """
74
56
  Number of ids that can be stored in the `jobsV2` attribute of
75
57
  `app.state`.
@@ -80,52 +62,6 @@ class Settings(BaseSettings):
80
62
  Waiting time for the shutdown phase of executors
81
63
  """
82
64
 
83
- FRACTAL_VIEWER_AUTHORIZATION_SCHEME: ViewerAuthScheme = "none"
84
- """
85
- Defines how the list of allowed viewer paths is built.
86
-
87
- This variable affects the `GET /auth/current-user/allowed-viewer-paths/`
88
- response, which is then consumed by
89
- [fractal-vizarr-viewer](https://github.com/fractal-analytics-platform/fractal-vizarr-viewer).
90
-
91
- Options:
92
-
93
- - "viewer-paths": The list of allowed viewer paths will include the user's
94
- `project_dir` along with any path defined in user groups' `viewer_paths`
95
- attributes.
96
- - "users-folders": The list will consist of the user's `project_dir` and a
97
- user-specific folder. The user folder is constructed by concatenating
98
- the base folder `FRACTAL_VIEWER_BASE_FOLDER` with the user's
99
- `slurm_user`.
100
- - "none": An empty list will be returned, indicating no access to
101
- viewer paths. Useful when vizarr viewer is not used.
102
- """
103
-
104
- FRACTAL_VIEWER_BASE_FOLDER: AbsolutePathStr | None = None
105
- """
106
- Base path to Zarr files that will be served by fractal-vizarr-viewer;
107
- This variable is required and used only when
108
- FRACTAL_VIEWER_AUTHORIZATION_SCHEME is set to "users-folders".
109
- """
110
-
111
- def check(self):
112
- """
113
- Make sure that required variables are set
114
-
115
- This method must be called before the server starts
116
- """
117
- # FRACTAL_VIEWER_BASE_FOLDER is required when
118
- # FRACTAL_VIEWER_AUTHORIZATION_SCHEME is set to "users-folders"
119
- # and it must be an absolute path
120
- if self.FRACTAL_VIEWER_AUTHORIZATION_SCHEME == "users-folders":
121
- viewer_base_folder = self.FRACTAL_VIEWER_BASE_FOLDER
122
- if viewer_base_folder is None:
123
- raise FractalConfigurationError(
124
- "FRACTAL_VIEWER_BASE_FOLDER is required when "
125
- "FRACTAL_VIEWER_AUTHORIZATION_SCHEME is set to "
126
- "users-folders"
127
- )
128
-
129
65
  FRACTAL_HELP_URL: HttpUrl | None = None
130
66
  """
131
67
  The URL of an instance-specific Fractal help page.
@@ -37,7 +37,7 @@ class OAuthSettings(BaseSettings):
37
37
  """
38
38
  Secret to authorise against the identity provider.
39
39
  """
40
- OAUTH_OIDC_CONFIG_ENDPOINT: str | None = None
40
+ OAUTH_OIDC_CONFIG_ENDPOINT: SecretStr | None = None
41
41
  """
42
42
  OpenID configuration endpoint, for autodiscovery of relevant endpoints.
43
43
  """
@@ -55,7 +55,7 @@ class OAuthSettings(BaseSettings):
55
55
  and self.OAUTH_OIDC_CONFIG_ENDPOINT is None
56
56
  ):
57
57
  raise ValueError(
58
- f"{self.OAUTH_OIDC_CONFIG_ENDPOINT=} but "
58
+ f"self.OAUTH_OIDC_CONFIG_ENDPOINT=None but "
59
59
  f"{self.OAUTH_CLIENT_NAME=}"
60
60
  )
61
61
  return self
fractal_server/main.py CHANGED
@@ -6,6 +6,7 @@ from fastapi import FastAPI
6
6
 
7
7
  from .app.routes.aux._runner import _backend_supports_shutdown
8
8
  from .app.shutdown import cleanup_after_shutdown
9
+ from .config import get_data_settings
9
10
  from .config import get_db_settings
10
11
  from .config import get_email_settings
11
12
  from .config import get_settings
@@ -50,16 +51,16 @@ def check_settings() -> None:
50
51
  ValidationError: If the configuration is invalid.
51
52
  """
52
53
  settings = Inject(get_settings)
53
- settings.check()
54
54
  db_settings = Inject(get_db_settings)
55
55
  email_settings = Inject(get_email_settings)
56
-
56
+ data_settings = Inject(get_data_settings)
57
57
  logger = set_logger("fractal_server_settings")
58
58
  logger.debug("Fractal Settings:")
59
59
  for key, value in chain(
60
60
  db_settings.model_dump().items(),
61
61
  settings.model_dump().items(),
62
62
  email_settings.model_dump().items(),
63
+ data_settings.model_dump().items(),
63
64
  ):
64
65
  if any(s in key.upper() for s in ["PASSWORD", "SECRET", "KEY"]):
65
66
  value = "*****"
@@ -1,7 +1,7 @@
1
1
  NAMING_CONVENTION = {
2
2
  "ix": "ix_%(column_0_label)s",
3
3
  "uq": "uq_%(table_name)s_%(column_0_name)s",
4
- "ck": "ck_%(table_name)s_`%(constraint_name)s`",
4
+ "ck": "ck_%(table_name)s_%(constraint_name)s",
5
5
  "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
6
6
  "pk": "pk_%(table_name)s",
7
7
  }
@@ -1,8 +1,8 @@
1
- """resource-profile
1
+ """2.17.0
2
2
 
3
- Revision ID: a80ac5a352bf
3
+ Revision ID: 83bc2ad3ffcc
4
4
  Revises: 981d588fe248
5
- Create Date: 2025-10-15 15:53:34.823398
5
+ Create Date: 2025-10-30 14:16:53.639006
6
6
 
7
7
  """
8
8
  import sqlalchemy as sa
@@ -11,7 +11,7 @@ from alembic import op
11
11
  from sqlalchemy.dialects import postgresql
12
12
 
13
13
  # revision identifiers, used by Alembic.
14
- revision = "a80ac5a352bf"
14
+ revision = "83bc2ad3ffcc"
15
15
  down_revision = "981d588fe248"
16
16
  branch_labels = None
17
17
  depends_on = None
@@ -64,13 +64,11 @@ def upgrade() -> None:
64
64
  ),
65
65
  sa.CheckConstraint(
66
66
  "(type = 'local') OR (jobs_slurm_python_worker IS NOT NULL)",
67
- name=op.f(
68
- "ck_resource_`ck_resource_jobs_slurm_python_worker_set`"
69
- ),
67
+ name=op.f("ck_resource_jobs_slurm_python_worker_set"),
70
68
  ),
71
69
  sa.CheckConstraint(
72
70
  "type IN ('local', 'slurm_sudo', 'slurm_ssh')",
73
- name=op.f("ck_resource_`ck_resource_correct_type`"),
71
+ name=op.f("ck_resource_correct_type"),
74
72
  ),
75
73
  sa.PrimaryKeyConstraint("id", name=op.f("pk_resource")),
76
74
  sa.UniqueConstraint("name", name=op.f("uq_resource_name")),
@@ -103,7 +101,7 @@ def upgrade() -> None:
103
101
  ["resource_id"],
104
102
  ["resource.id"],
105
103
  name=op.f("fk_profile_resource_id_resource"),
106
- ondelete="CASCADE",
104
+ ondelete="RESTRICT",
107
105
  ),
108
106
  sa.PrimaryKeyConstraint("id", name=op.f("pk_profile")),
109
107
  sa.UniqueConstraint("name", name=op.f("uq_profile_name")),
@@ -117,7 +115,7 @@ def upgrade() -> None:
117
115
  "resource",
118
116
  ["resource_id"],
119
117
  ["id"],
120
- ondelete="SET NULL",
118
+ ondelete="RESTRICT",
121
119
  )
122
120
 
123
121
  with op.batch_alter_table("taskgroupv2", schema=None) as batch_op:
@@ -129,52 +127,54 @@ def upgrade() -> None:
129
127
  "resource",
130
128
  ["resource_id"],
131
129
  ["id"],
132
- ondelete="SET NULL",
130
+ ondelete="RESTRICT",
133
131
  )
134
132
 
135
133
  with op.batch_alter_table("user_oauth", schema=None) as batch_op:
136
134
  batch_op.add_column(
137
135
  sa.Column("profile_id", sa.Integer(), nullable=True)
138
136
  )
137
+ batch_op.add_column(
138
+ sa.Column(
139
+ "project_dir",
140
+ sa.String(),
141
+ server_default="/PLACEHOLDER",
142
+ nullable=False,
143
+ )
144
+ )
145
+ batch_op.add_column(
146
+ sa.Column(
147
+ "slurm_accounts",
148
+ postgresql.ARRAY(sa.String()),
149
+ server_default="{}",
150
+ nullable=True,
151
+ )
152
+ )
139
153
  batch_op.create_foreign_key(
140
154
  batch_op.f("fk_user_oauth_profile_id_profile"),
141
155
  "profile",
142
156
  ["profile_id"],
143
157
  ["id"],
144
- ondelete="SET NULL",
158
+ ondelete="RESTRICT",
145
159
  )
146
-
147
- with op.batch_alter_table("user_settings", schema=None) as batch_op:
148
- batch_op.drop_column("ssh_jobs_dir")
149
- batch_op.drop_column("ssh_tasks_dir")
160
+ batch_op.drop_column("username")
150
161
 
151
162
  # ### end Alembic commands ###
152
163
 
153
164
 
154
165
  def downgrade() -> None:
155
166
  # ### commands auto generated by Alembic - please adjust! ###
156
- with op.batch_alter_table("user_settings", schema=None) as batch_op:
157
- batch_op.add_column(
158
- sa.Column(
159
- "ssh_tasks_dir",
160
- sa.VARCHAR(),
161
- autoincrement=False,
162
- nullable=True,
163
- )
164
- )
167
+ with op.batch_alter_table("user_oauth", schema=None) as batch_op:
165
168
  batch_op.add_column(
166
169
  sa.Column(
167
- "ssh_jobs_dir",
168
- sa.VARCHAR(),
169
- autoincrement=False,
170
- nullable=True,
170
+ "username", sa.VARCHAR(), autoincrement=False, nullable=True
171
171
  )
172
172
  )
173
-
174
- with op.batch_alter_table("user_oauth", schema=None) as batch_op:
175
173
  batch_op.drop_constraint(
176
174
  batch_op.f("fk_user_oauth_profile_id_profile"), type_="foreignkey"
177
175
  )
176
+ batch_op.drop_column("slurm_accounts")
177
+ batch_op.drop_column("project_dir")
178
178
  batch_op.drop_column("profile_id")
179
179
 
180
180
  with op.batch_alter_table("taskgroupv2", schema=None) as batch_op:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractal-server
3
- Version: 2.17.0a5
3
+ Version: 2.17.0a7
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License-Expression: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -13,18 +13,17 @@ Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Dist: alembic (>=1.13.1,<2.0.0)
15
15
  Requires-Dist: fabric (>=3.2.2,<3.3.0)
16
- Requires-Dist: fastapi (>=0.118.0,<0.119.0)
17
- Requires-Dist: fastapi-users[oauth] (>=14,<15)
16
+ Requires-Dist: fastapi (>=0.120.0,<0.121.0)
17
+ Requires-Dist: fastapi-users[oauth] (>=15,<16)
18
18
  Requires-Dist: gunicorn (>=23.0,<24.0)
19
19
  Requires-Dist: packaging (>=25.0.0,<26.0.0)
20
20
  Requires-Dist: psycopg[binary] (>=3.1.0,<4.0.0)
21
21
  Requires-Dist: pydantic (>=2.12.0,<2.13.0)
22
22
  Requires-Dist: pydantic-settings (==2.11.0)
23
- Requires-Dist: python-dotenv (>=1.1.0,<1.2.0)
24
23
  Requires-Dist: sqlalchemy[asyncio] (>=2.0.23,<2.1)
25
24
  Requires-Dist: sqlmodel (==0.0.27)
26
25
  Requires-Dist: tomli_w (>=1.2.0,<1.3.0)
27
- Requires-Dist: uvicorn (>=0.37.0,<0.38.0)
26
+ Requires-Dist: uvicorn (>=0.38.0,<0.39.0)
28
27
  Requires-Dist: uvicorn-worker (==0.4.0)
29
28
  Project-URL: Documentation, https://fractal-analytics-platform.github.io/fractal-server
30
29
  Project-URL: Homepage, https://github.com/fractal-analytics-platform/fractal-server