fractal-server 2.17.0a8__py3-none-any.whl → 2.17.0a10__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.
@@ -1 +1 @@
1
- __VERSION__ = "2.17.0a8"
1
+ __VERSION__ = "2.17.0a10"
@@ -62,7 +62,7 @@ async def get_resource_list(
62
62
  Query `Resource` table.
63
63
  """
64
64
 
65
- stm = select(Resource)
65
+ stm = select(Resource).order_by(Resource.id)
66
66
  res = await db.execute(stm)
67
67
  resource_list = res.scalars().all()
68
68
 
@@ -103,6 +103,7 @@ async def query_tasks(
103
103
  .where(TaskGroupV2.resource_id == resource_id)
104
104
  )
105
105
 
106
+ stm = stm.order_by(TaskV2.id)
106
107
  res = await db.execute(stm)
107
108
  task_list = res.scalars().all()
108
109
  if len(task_list) > max_number_of_results:
@@ -133,6 +133,7 @@ async def query_task_group_list(
133
133
  if resource_id is not None:
134
134
  stm = stm.where(TaskGroupV2.resource_id == resource_id)
135
135
 
136
+ stm = stm.order_by(TaskGroupV2.id)
136
137
  res = await db.execute(stm)
137
138
  task_groups_list = res.scalars().all()
138
139
  return task_groups_list
@@ -20,7 +20,9 @@ from fractal_server.app.models.v2 import WorkflowTaskV2
20
20
  from fractal_server.app.routes.api.v2._aux_functions import (
21
21
  _get_user_resource_id,
22
22
  )
23
- from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
23
+ from fractal_server.app.routes.auth._aux_auth import (
24
+ _get_default_usergroup_id_or_none,
25
+ )
24
26
  from fractal_server.app.routes.auth._aux_auth import (
25
27
  _verify_user_belongs_to_group,
26
28
  )
@@ -73,7 +75,7 @@ async def _get_task_group_read_access(
73
75
  status_code=status.HTTP_403_FORBIDDEN,
74
76
  detail=(
75
77
  "Current user has no read access to TaskGroupV2 "
76
- f"{task_group_id}.",
78
+ f"{task_group_id}."
77
79
  ),
78
80
  )
79
81
 
@@ -90,7 +92,7 @@ async def _get_task_group_read_access(
90
92
  .where(LinkUserGroup.user_id == user_id)
91
93
  .where(UserOAuth.id == user_id)
92
94
  .where(Profile.id == UserOAuth.profile_id)
93
- .where(TaskGroupV2.resource_id == Profile.resource_id)
95
+ .where(task_group.resource_id == Profile.resource_id)
94
96
  )
95
97
  res = await db.execute(stm)
96
98
  link = res.unique().scalars().one_or_none()
@@ -237,7 +239,7 @@ async def _get_valid_user_group_id(
237
239
  elif private is True:
238
240
  user_group_id = None
239
241
  elif user_group_id is None:
240
- user_group_id = await _get_default_usergroup_id(db=db)
242
+ user_group_id = await _get_default_usergroup_id_or_none(db=db)
241
243
  else:
242
244
  await _verify_user_belongs_to_group(
243
245
  user_id=user_id, user_group_id=user_group_id, db=db
@@ -370,7 +372,9 @@ async def _verify_non_duplication_group_constraint(
370
372
 
371
373
 
372
374
  async def _verify_non_duplication_group_path(
375
+ *,
373
376
  path: str | None,
377
+ resource_id: int,
374
378
  db: AsyncSession,
375
379
  ) -> None:
376
380
  """
@@ -378,7 +382,11 @@ async def _verify_non_duplication_group_path(
378
382
  """
379
383
  if path is None:
380
384
  return
381
- stm = select(TaskGroupV2.id).where(TaskGroupV2.path == path)
385
+ stm = (
386
+ select(TaskGroupV2.id)
387
+ .where(TaskGroupV2.path == path)
388
+ .where(TaskGroupV2.resource_id == resource_id)
389
+ )
382
390
  res = await db.execute(stm)
383
391
  duplicate_ids = res.scalars().all()
384
392
  if duplicate_ids:
@@ -5,8 +5,10 @@ from sqlmodel import select
5
5
  from fractal_server.app.db import AsyncSession
6
6
  from fractal_server.app.models import LinkUserGroup
7
7
  from fractal_server.app.models.v2 import TaskGroupV2
8
+ from fractal_server.config import get_settings
8
9
  from fractal_server.exceptions import UnreachableBranchError
9
10
  from fractal_server.logger import set_logger
11
+ from fractal_server.syringe import Inject
10
12
 
11
13
 
12
14
  logger = set_logger(__name__)
@@ -16,7 +18,7 @@ async def _disambiguate_task_groups(
16
18
  *,
17
19
  matching_task_groups: list[TaskGroupV2],
18
20
  user_id: int,
19
- default_group_id: int,
21
+ default_group_id: int | None,
20
22
  db: AsyncSession,
21
23
  ) -> TaskGroupV2 | None:
22
24
  """
@@ -49,21 +51,23 @@ async def _disambiguate_task_groups(
49
51
  )
50
52
 
51
53
  # Medium priority: task groups owned by default user group
54
+ settings = Inject(get_settings)
52
55
  list_user_group_ids = [tg.user_group_id for tg in matching_task_groups]
53
- try:
54
- ind_user_group_id = list_user_group_ids.index(default_group_id)
55
- task_group = matching_task_groups[ind_user_group_id]
56
- logger.debug(
57
- "[_disambiguate_task_groups] "
58
- f"Found task group {task_group.id} with {user_id=}, return."
59
- )
60
- return task_group
61
- except ValueError:
62
- logger.debug(
63
- "[_disambiguate_task_groups] "
64
- "No task group with user_group_id="
65
- f"{default_group_id}, continue."
66
- )
56
+ if settings.FRACTAL_DEFAULT_GROUP_NAME is not None:
57
+ try:
58
+ ind_user_group_id = list_user_group_ids.index(default_group_id)
59
+ task_group = matching_task_groups[ind_user_group_id]
60
+ logger.debug(
61
+ "[_disambiguate_task_groups] "
62
+ f"Found task group {task_group.id} with {user_id=}, return."
63
+ )
64
+ return task_group
65
+ except ValueError:
66
+ logger.debug(
67
+ "[_disambiguate_task_groups] "
68
+ "No task group with user_group_id="
69
+ f"{default_group_id}, continue."
70
+ )
67
71
 
68
72
  # Lowest priority: task groups owned by other groups, sorted
69
73
  # according to age of the user/usergroup link
@@ -97,7 +101,7 @@ async def _disambiguate_task_groups_not_none(
97
101
  *,
98
102
  matching_task_groups: list[TaskGroupV2],
99
103
  user_id: int,
100
- default_group_id: int,
104
+ default_group_id: int | None,
101
105
  db: AsyncSession,
102
106
  ) -> TaskGroupV2:
103
107
  """
@@ -133,7 +137,7 @@ async def remove_duplicate_task_groups(
133
137
  *,
134
138
  task_groups: list[TaskGroupV2],
135
139
  user_id: int,
136
- default_group_id: int,
140
+ default_group_id: int | None,
137
141
  db: AsyncSession,
138
142
  ) -> list[TaskGroupV2]:
139
143
  """
@@ -73,6 +73,7 @@ async def get_list_task(
73
73
  if author is not None:
74
74
  stm = stm.where(TaskV2.authors.icontains(author))
75
75
 
76
+ stm = stm.order_by(TaskV2.id)
76
77
  res = await db.execute(stm)
77
78
  task_list = list(res.scalars().all())
78
79
  await db.close()
@@ -200,8 +200,8 @@ async def collect_tasks_pip(
200
200
  raise HTTPException(
201
201
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
202
202
  detail=(
203
- f"Python version {task_group_attrs['python_version']} is "
204
- "not available for Fractal task collection."
203
+ f"Python version {task_group_attrs['python_version']} "
204
+ "is not available on this Fractal instance."
205
205
  ),
206
206
  )
207
207
 
@@ -305,6 +305,7 @@ async def collect_tasks_pip(
305
305
  )
306
306
  await _verify_non_duplication_group_path(
307
307
  path=task_group_attrs["path"],
308
+ resource_id=resource_id,
308
309
  db=db,
309
310
  )
310
311
 
@@ -159,6 +159,7 @@ async def collect_task_pixi(
159
159
  )
160
160
  await _verify_non_duplication_group_path(
161
161
  path=task_group_attrs["path"],
162
+ resource_id=resource_id,
162
163
  db=db,
163
164
  )
164
165
 
@@ -11,6 +11,7 @@ from pydantic.types import AwareDatetime
11
11
  from sqlmodel import or_
12
12
  from sqlmodel import select
13
13
 
14
+ from ._aux_functions import _get_user_resource_id
14
15
  from ._aux_functions_tasks import _get_task_group_full_access
15
16
  from ._aux_functions_tasks import _get_task_group_read_access
16
17
  from ._aux_functions_tasks import _verify_non_duplication_group_constraint
@@ -22,7 +23,9 @@ from fractal_server.app.models import UserOAuth
22
23
  from fractal_server.app.models.v2 import TaskGroupActivityV2
23
24
  from fractal_server.app.models.v2 import TaskGroupV2
24
25
  from fractal_server.app.routes.auth import current_user_act_ver_prof
25
- from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
26
+ from fractal_server.app.routes.auth._aux_auth import (
27
+ _get_default_usergroup_id_or_none,
28
+ )
26
29
  from fractal_server.app.routes.auth._aux_auth import (
27
30
  _verify_user_belongs_to_group,
28
31
  )
@@ -142,7 +145,14 @@ async def get_task_group_list(
142
145
  )
143
146
  ),
144
147
  )
145
- stm = select(TaskGroupV2).where(condition).order_by(TaskGroupV2.pkg_name)
148
+
149
+ user_resource_id = await _get_user_resource_id(user_id=user.id, db=db)
150
+ stm = (
151
+ select(TaskGroupV2)
152
+ .where(TaskGroupV2.resource_id == user_resource_id)
153
+ .where(condition)
154
+ .order_by(TaskGroupV2.pkg_name)
155
+ )
146
156
  if only_active:
147
157
  stm = stm.where(TaskGroupV2.active)
148
158
 
@@ -155,7 +165,7 @@ async def get_task_group_list(
155
165
  setattr(task, "args_schema_non_parallel", None)
156
166
  setattr(task, "args_schema_parallel", None)
157
167
 
158
- default_group_id = await _get_default_usergroup_id(db)
168
+ default_group_id = await _get_default_usergroup_id_or_none(db)
159
169
  grouped_result = [
160
170
  (
161
171
  pkg_name,
@@ -15,6 +15,7 @@ from ....schemas.v2 import WorkflowReadV2WithWarnings
15
15
  from ....schemas.v2 import WorkflowTaskCreateV2
16
16
  from ._aux_functions import _check_workflow_exists
17
17
  from ._aux_functions import _get_project_check_owner
18
+ from ._aux_functions import _get_user_resource_id
18
19
  from ._aux_functions import _workflow_insert_task
19
20
  from ._aux_functions_tasks import _add_warnings_to_workflow_tasks
20
21
  from ._aux_functions_tasks import _check_type_filters_compatibility
@@ -25,7 +26,9 @@ from fractal_server.app.routes.api.v2._aux_task_group_disambiguation import (
25
26
  _disambiguate_task_groups,
26
27
  )
27
28
  from fractal_server.app.routes.auth import current_user_act_ver_prof
28
- from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
29
+ from fractal_server.app.routes.auth._aux_auth import (
30
+ _get_default_usergroup_id_or_none,
31
+ )
29
32
  from fractal_server.app.schemas.v2 import TaskImportV2
30
33
  from fractal_server.logger import set_logger
31
34
 
@@ -38,20 +41,26 @@ logger = set_logger(__name__)
38
41
  async def _get_user_accessible_taskgroups(
39
42
  *,
40
43
  user_id: int,
44
+ user_resource_id: int,
41
45
  db: AsyncSession,
42
46
  ) -> list[TaskGroupV2]:
43
47
  """
44
48
  Retrieve list of task groups that the user has access to.
45
49
  """
46
- stm = select(TaskGroupV2).where(
47
- or_(
48
- TaskGroupV2.user_id == user_id,
49
- TaskGroupV2.user_group_id.in_(
50
- select(LinkUserGroup.group_id).where(
51
- LinkUserGroup.user_id == user_id
52
- )
53
- ),
50
+
51
+ stm = (
52
+ select(TaskGroupV2)
53
+ .where(
54
+ or_(
55
+ TaskGroupV2.user_id == user_id,
56
+ TaskGroupV2.user_group_id.in_(
57
+ select(LinkUserGroup.group_id).where(
58
+ LinkUserGroup.user_id == user_id
59
+ )
60
+ ),
61
+ )
54
62
  )
63
+ .where(TaskGroupV2.resource_id == user_resource_id)
55
64
  )
56
65
  res = await db.execute(stm)
57
66
  accessible_task_groups = res.scalars().all()
@@ -93,7 +102,7 @@ async def _get_task_by_taskimport(
93
102
  task_import: TaskImportV2,
94
103
  task_groups_list: list[TaskGroupV2],
95
104
  user_id: int,
96
- default_group_id: int,
105
+ default_group_id: int | None,
97
106
  db: AsyncSession,
98
107
  ) -> int | None:
99
108
  """
@@ -212,6 +221,8 @@ async def import_workflow(
212
221
  Import an existing workflow into a project and create required objects.
213
222
  """
214
223
 
224
+ user_resource_id = await _get_user_resource_id(user_id=user.id, db=db)
225
+
215
226
  # Preliminary checks
216
227
  await _get_project_check_owner(
217
228
  project_id=project_id,
@@ -227,8 +238,9 @@ async def import_workflow(
227
238
  task_group_list = await _get_user_accessible_taskgroups(
228
239
  user_id=user.id,
229
240
  db=db,
241
+ user_resource_id=user_resource_id,
230
242
  )
231
- default_group_id = await _get_default_usergroup_id(db)
243
+ default_group_id = await _get_default_usergroup_id_or_none(db)
232
244
 
233
245
  list_wf_tasks = []
234
246
  list_task_ids = []
@@ -9,8 +9,10 @@ from fractal_server.app.models.security import UserGroup
9
9
  from fractal_server.app.models.security import UserOAuth
10
10
  from fractal_server.app.schemas.user import UserRead
11
11
  from fractal_server.app.schemas.user_group import UserGroupRead
12
- from fractal_server.app.security import FRACTAL_DEFAULT_GROUP_NAME
12
+ from fractal_server.config import get_settings
13
13
  from fractal_server.logger import set_logger
14
+ from fractal_server.syringe import Inject
15
+
14
16
 
15
17
  logger = set_logger(__name__)
16
18
 
@@ -29,6 +31,9 @@ async def _get_single_user_with_groups(
29
31
  Returns:
30
32
  A `UserRead` object with `group_ids_names` dict
31
33
  """
34
+
35
+ settings = Inject(get_settings)
36
+
32
37
  stm_groups = (
33
38
  select(UserGroup)
34
39
  .join(LinkUserGroup)
@@ -39,25 +44,25 @@ async def _get_single_user_with_groups(
39
44
  groups = res.scalars().unique().all()
40
45
  group_ids_names = [(group.id, group.name) for group in groups]
41
46
 
42
- # Check that Fractal Default Group is the first of the list. If not, fix.
47
+ # Identify the default-group position in the list of groups
43
48
  index = next(
44
49
  (
45
- i
46
- for i, group_tuple in enumerate(group_ids_names)
47
- if group_tuple[1] == FRACTAL_DEFAULT_GROUP_NAME
50
+ ind
51
+ for ind, group_tuple in enumerate(group_ids_names)
52
+ if group_tuple[1] == settings.FRACTAL_DEFAULT_GROUP_NAME
48
53
  ),
49
54
  None,
50
55
  )
51
- if index is None:
52
- logger.warning(
53
- f"User {user.id} not in "
54
- f"default UserGroup '{FRACTAL_DEFAULT_GROUP_NAME}'"
55
- )
56
- elif index != 0:
56
+ if (index is None) or (index == 0):
57
+ # Either the default group does not exist, or it is already the first
58
+ # one. No action needed.
59
+ pass
60
+ else:
61
+ # Move the default group to the first position
57
62
  default_group = group_ids_names.pop(index)
58
63
  group_ids_names.insert(0, default_group)
59
- else:
60
- pass
64
+
65
+ # Create dump of `user.oauth_accounts` relationship
61
66
  oauth_accounts = [
62
67
  oauth_account.model_dump() for oauth_account in user.oauth_accounts
63
68
  ]
@@ -121,17 +126,31 @@ async def _usergroup_or_404(usergroup_id: int, db: AsyncSession) -> UserGroup:
121
126
  return user
122
127
 
123
128
 
124
- async def _get_default_usergroup_id(db: AsyncSession) -> int:
129
+ async def _get_default_usergroup_id_or_none(db: AsyncSession) -> int | None:
130
+ """
131
+ Return the ID of the group named `"All"`, if `FRACTAL_DEFAULT_GROUP_NAME`
132
+ is set and such group exists. Return `None`, if
133
+ `FRACTAL_DEFAULT_GROUP_NAME=None` or if the `"All"` group does not exist.
134
+ """
135
+ settings = Inject(get_settings)
125
136
  stm = select(UserGroup.id).where(
126
- UserGroup.name == FRACTAL_DEFAULT_GROUP_NAME
137
+ UserGroup.name == settings.FRACTAL_DEFAULT_GROUP_NAME
127
138
  )
128
139
  res = await db.execute(stm)
129
140
  user_group_id = res.scalars().one_or_none()
130
- if user_group_id is None:
141
+
142
+ if (
143
+ settings.FRACTAL_DEFAULT_GROUP_NAME is not None
144
+ and user_group_id is None
145
+ ):
131
146
  raise HTTPException(
132
147
  status_code=status.HTTP_404_NOT_FOUND,
133
- detail=f"User group '{FRACTAL_DEFAULT_GROUP_NAME}' not found.",
148
+ detail=(
149
+ f"User group '{settings.FRACTAL_DEFAULT_GROUP_NAME}'"
150
+ " not found.",
151
+ ),
134
152
  )
153
+
135
154
  return user_group_id
136
155
 
137
156
 
@@ -15,6 +15,7 @@ from fractal_server.app.models import Resource
15
15
  from fractal_server.app.models import UserGroup
16
16
  from fractal_server.app.models import UserOAuth
17
17
  from fractal_server.app.routes.auth import current_user_act
18
+ from fractal_server.app.routes.auth import current_user_act_ver
18
19
  from fractal_server.app.routes.auth._aux_auth import (
19
20
  _get_single_user_with_groups,
20
21
  )
@@ -111,7 +112,7 @@ async def get_current_user_profile_info(
111
112
  "/current-user/allowed-viewer-paths/", response_model=list[str]
112
113
  )
113
114
  async def get_current_user_allowed_viewer_paths(
114
- current_user: UserOAuth = Depends(current_user_act),
115
+ current_user: UserOAuth = Depends(current_user_act_ver),
115
116
  db: AsyncSession = Depends(get_async_db),
116
117
  ) -> list[str]:
117
118
  """
@@ -10,7 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
10
10
  from sqlmodel import select
11
11
 
12
12
  from . import current_superuser_act
13
- from ._aux_auth import _get_default_usergroup_id
13
+ from ._aux_auth import _get_default_usergroup_id_or_none
14
14
  from ._aux_auth import _get_single_usergroup_with_user_ids
15
15
  from ._aux_auth import _user_or_404
16
16
  from ._aux_auth import _usergroup_or_404
@@ -21,11 +21,13 @@ from fractal_server.app.models import UserOAuth
21
21
  from fractal_server.app.schemas.user_group import UserGroupCreate
22
22
  from fractal_server.app.schemas.user_group import UserGroupRead
23
23
  from fractal_server.app.schemas.user_group import UserGroupUpdate
24
- from fractal_server.app.security import FRACTAL_DEFAULT_GROUP_NAME
24
+ from fractal_server.config import get_settings
25
25
  from fractal_server.logger import set_logger
26
+ from fractal_server.syringe import Inject
26
27
 
27
28
  logger = set_logger(__name__)
28
29
 
30
+
29
31
  router_group = APIRouter()
30
32
 
31
33
 
@@ -38,7 +40,7 @@ async def get_list_user_groups(
38
40
  db: AsyncSession = Depends(get_async_db),
39
41
  ) -> list[UserGroupRead]:
40
42
  # Get all groups
41
- stm_all_groups = select(UserGroup)
43
+ stm_all_groups = select(UserGroup).order_by(UserGroup.id)
42
44
  res = await db.execute(stm_all_groups)
43
45
  groups = res.scalars().all()
44
46
 
@@ -138,19 +140,24 @@ async def delete_single_group(
138
140
  user: UserOAuth = Depends(current_superuser_act),
139
141
  db: AsyncSession = Depends(get_async_db),
140
142
  ) -> Response:
143
+ """
144
+ Delete a user group.
145
+
146
+ If `FRACTAL_DEFAULT_GROUP_NAME="All"`, a group named `"All"` cannot be
147
+ deleted. If `FRACTAL_DEFAULT_GROUP_NAME=None`, any group can be deleted.
148
+ """
149
+ settings = Inject(get_settings)
141
150
  group = await _usergroup_or_404(group_id, db)
142
151
 
143
- if group.name == FRACTAL_DEFAULT_GROUP_NAME:
152
+ if group.name == settings.FRACTAL_DEFAULT_GROUP_NAME:
144
153
  raise HTTPException(
145
154
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
146
155
  detail=(
147
156
  "Cannot delete default UserGroup "
148
- f"'{FRACTAL_DEFAULT_GROUP_NAME}'."
157
+ f"'{settings.FRACTAL_DEFAULT_GROUP_NAME}'."
149
158
  ),
150
159
  )
151
160
 
152
- # Delete
153
-
154
161
  await db.delete(group)
155
162
  await db.commit()
156
163
 
@@ -188,18 +195,21 @@ async def remove_user_from_group(
188
195
  superuser: UserOAuth = Depends(current_superuser_act),
189
196
  db: AsyncSession = Depends(get_async_db),
190
197
  ) -> UserGroupRead:
198
+ settings = Inject(get_settings)
191
199
  # Check that user and group exist
192
200
  await _usergroup_or_404(group_id, db)
193
201
  user = await _user_or_404(user_id, db)
194
202
 
195
203
  # Check that group is not the default one
196
- default_user_group_id = await _get_default_usergroup_id(db=db)
197
- if default_user_group_id == group_id:
204
+ default_user_group_id_or_none = await _get_default_usergroup_id_or_none(
205
+ db=db
206
+ )
207
+ if default_user_group_id_or_none == group_id:
198
208
  raise HTTPException(
199
209
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
200
210
  detail=(
201
- f"Cannot remove user from '{FRACTAL_DEFAULT_GROUP_NAME}' "
202
- "group.",
211
+ f"Cannot remove user from "
212
+ f"'{settings.FRACTAL_DEFAULT_GROUP_NAME}' group.",
203
213
  ),
204
214
  )
205
215
 
@@ -15,9 +15,8 @@ from . import current_superuser_act
15
15
  from ...db import get_async_db
16
16
  from ...schemas.user import UserRead
17
17
  from ...schemas.user import UserUpdate
18
- from ._aux_auth import _get_default_usergroup_id
18
+ from ._aux_auth import _get_default_usergroup_id_or_none
19
19
  from ._aux_auth import _get_single_user_with_groups
20
- from ._aux_auth import FRACTAL_DEFAULT_GROUP_NAME
21
20
  from fractal_server.app.models import LinkUserGroup
22
21
  from fractal_server.app.models import UserGroup
23
22
  from fractal_server.app.models import UserOAuth
@@ -26,7 +25,10 @@ from fractal_server.app.routes.auth._aux_auth import _user_or_404
26
25
  from fractal_server.app.schemas.user import UserUpdateGroups
27
26
  from fractal_server.app.security import get_user_manager
28
27
  from fractal_server.app.security import UserManager
28
+ from fractal_server.config import get_settings
29
29
  from fractal_server.logger import set_logger
30
+ from fractal_server.syringe import Inject
31
+
30
32
 
31
33
  router_users = APIRouter()
32
34
 
@@ -146,6 +148,7 @@ async def set_user_groups(
146
148
  superuser: UserOAuth = Depends(current_superuser_act),
147
149
  db: AsyncSession = Depends(get_async_db),
148
150
  ) -> UserRead:
151
+ settings = Inject(get_settings)
149
152
  # Preliminary check that all objects exist in the db
150
153
  user = await _user_or_404(user_id=user_id, db=db)
151
154
  target_group_ids = user_update.group_ids
@@ -161,13 +164,16 @@ async def set_user_groups(
161
164
  )
162
165
 
163
166
  # Check that default group is not being removed
164
- default_group_id = await _get_default_usergroup_id(db=db)
165
- if default_group_id not in target_group_ids:
167
+ default_group_id_or_none = await _get_default_usergroup_id_or_none(db=db)
168
+ if (
169
+ default_group_id_or_none is not None
170
+ and default_group_id_or_none not in target_group_ids
171
+ ):
166
172
  raise HTTPException(
167
173
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
168
174
  detail=(
169
175
  f"Cannot remove user from "
170
- f"'{FRACTAL_DEFAULT_GROUP_NAME}' group.",
176
+ f"'{settings.FRACTAL_DEFAULT_GROUP_NAME}' group.",
171
177
  ),
172
178
  )
173
179
 
@@ -7,6 +7,7 @@ from pydantic import field_serializer
7
7
  from pydantic.types import AwareDatetime
8
8
 
9
9
  from fractal_server.types import ListUniqueAbsolutePathStr
10
+ from fractal_server.types import NonEmptyStr
10
11
 
11
12
  __all__ = (
12
13
  "UserGroupRead",
@@ -50,7 +51,7 @@ class UserGroupCreate(BaseModel):
50
51
 
51
52
  model_config = ConfigDict(extra="forbid")
52
53
 
53
- name: str
54
+ name: NonEmptyStr
54
55
  viewer_paths: ListUniqueAbsolutePathStr = Field(default_factory=list)
55
56
 
56
57
 
@@ -51,13 +51,12 @@ from fractal_server.app.security.signup_email import (
51
51
  )
52
52
  from fractal_server.config import get_email_settings
53
53
  from fractal_server.config import get_settings
54
+ from fractal_server.logger import close_logger
54
55
  from fractal_server.logger import set_logger
55
56
  from fractal_server.syringe import Inject
56
57
 
57
58
  logger = set_logger(__name__)
58
59
 
59
- FRACTAL_DEFAULT_GROUP_NAME = "All"
60
-
61
60
 
62
61
  class SQLModelUserDatabaseAsync(Generic[UP, ID], BaseUserDatabase[UP, ID]):
63
62
  """
@@ -281,12 +280,12 @@ class UserManager(IntegerIDMixin, BaseUserManager[UserOAuth, int]):
281
280
  )
282
281
 
283
282
  # (1) Prepare user-facing error message
284
- settings = Inject(get_settings)
285
283
  error_msg = (
286
284
  "Thank you for registering for the Fractal service. "
287
285
  "Administrators have been informed to configure your "
288
286
  "account and will get back to you."
289
287
  )
288
+ settings = Inject(get_settings)
290
289
  if settings.FRACTAL_HELP_URL is not None:
291
290
  error_msg = (
292
291
  f"{error_msg}\n"
@@ -329,28 +328,38 @@ class UserManager(IntegerIDMixin, BaseUserManager[UserOAuth, int]):
329
328
  async def on_after_register(
330
329
  self, user: UserOAuth, request: Request | None = None
331
330
  ):
331
+ settings = Inject(get_settings)
332
332
  logger.info(
333
333
  f"New-user registration completed ({user.id=}, {user.email=})."
334
334
  )
335
335
  async for db in get_async_db():
336
- stm = select(UserGroup).where(
337
- UserGroup.name == FRACTAL_DEFAULT_GROUP_NAME
336
+ # Note: if `FRACTAL_DEFAULT_GROUP_NAME=None`, this query will
337
+ # result into `None`
338
+ settings = Inject(get_settings)
339
+ stm = select(UserGroup.id).where(
340
+ UserGroup.name == settings.FRACTAL_DEFAULT_GROUP_NAME
338
341
  )
339
342
  res = await db.execute(stm)
340
- default_group = res.scalar_one_or_none()
341
- if default_group is None:
342
- logger.warning(
343
- f"No group found with name {FRACTAL_DEFAULT_GROUP_NAME}"
344
- )
345
- else:
343
+ default_group_id_or_none = res.scalars().one_or_none()
344
+ if default_group_id_or_none is not None:
346
345
  link = LinkUserGroup(
347
- user_id=user.id, group_id=default_group.id
346
+ user_id=user.id, group_id=default_group_id_or_none
348
347
  )
349
348
  db.add(link)
350
349
  await db.commit()
351
350
  logger.info(
352
- f"Added {user.email} user to group {default_group.id=}."
351
+ f"Added {user.email} user to group "
352
+ f"{default_group_id_or_none=}."
353
353
  )
354
+ elif settings.FRACTAL_DEFAULT_GROUP_NAME is not None:
355
+ logger.error(
356
+ "No group found with name "
357
+ f"{settings.FRACTAL_DEFAULT_GROUP_NAME}"
358
+ )
359
+ # NOTE: the `else` of this branch would simply be a `pass`. The
360
+ # "All" group was not found, but this is not worth a WARNING
361
+ # because `FRACTAL_DEFAULT_GROUP_NAME` is set to `None` in the
362
+ # settings.
354
363
 
355
364
 
356
365
  async def get_user_manager(
@@ -433,34 +442,43 @@ async def _create_first_user(
433
442
  raise e
434
443
  finally:
435
444
  function_logger.info(f"END _create_first_user, with email '{email}'")
445
+ close_logger(function_logger)
436
446
 
437
447
 
438
448
  def _create_first_group():
439
449
  """
440
- Create a `UserGroup` with `name=FRACTAL_DEFAULT_GROUP_NAME`, if missing.
450
+ Create a `UserGroup` named `FRACTAL_DEFAULT_GROUP_NAME`, if this variable
451
+ is set and if such a group does not already exist.
441
452
  """
453
+ settings = Inject(get_settings)
442
454
  function_logger = set_logger("fractal_server.create_first_group")
443
455
 
456
+ if settings.FRACTAL_DEFAULT_GROUP_NAME is None:
457
+ function_logger.info(
458
+ f"SKIP because '{settings.FRACTAL_DEFAULT_GROUP_NAME=}'"
459
+ )
460
+ return
461
+
444
462
  function_logger.info(
445
- f"START _create_first_group, with name '{FRACTAL_DEFAULT_GROUP_NAME}'"
463
+ f"START, name '{settings.FRACTAL_DEFAULT_GROUP_NAME}'"
446
464
  )
447
465
  with next(get_sync_db()) as db:
448
466
  group_all = db.execute(
449
467
  select(UserGroup).where(
450
- UserGroup.name == FRACTAL_DEFAULT_GROUP_NAME
468
+ UserGroup.name == settings.FRACTAL_DEFAULT_GROUP_NAME
451
469
  )
452
470
  )
453
471
  if group_all.scalars().one_or_none() is None:
454
- first_group = UserGroup(name=FRACTAL_DEFAULT_GROUP_NAME)
472
+ first_group = UserGroup(name=settings.FRACTAL_DEFAULT_GROUP_NAME)
455
473
  db.add(first_group)
456
474
  db.commit()
457
475
  function_logger.info(
458
- f"Created group '{FRACTAL_DEFAULT_GROUP_NAME}'"
476
+ f"Created group '{settings.FRACTAL_DEFAULT_GROUP_NAME}'"
459
477
  )
460
478
  else:
461
479
  function_logger.info(
462
- f"Group '{FRACTAL_DEFAULT_GROUP_NAME}' already exists, skip."
480
+ f"Group '{settings.FRACTAL_DEFAULT_GROUP_NAME}' "
481
+ "already exists, skip."
463
482
  )
464
- function_logger.info(
465
- f"END _create_first_group, with name '{FRACTAL_DEFAULT_GROUP_NAME}'"
466
- )
483
+ function_logger.info("END")
484
+ close_logger(function_logger)
@@ -66,3 +66,13 @@ class Settings(BaseSettings):
66
66
  """
67
67
  The URL of an instance-specific Fractal help page.
68
68
  """
69
+
70
+ FRACTAL_DEFAULT_GROUP_NAME: Literal["All"] | None = None
71
+ """
72
+ Name of the default user group.
73
+
74
+ If set to `"All"`, then the user group with that name is a special user
75
+ group (e.g. it cannot be deleted, and new users are automatically added
76
+ to it). If set to `None` (the default value), then user groups are all
77
+ equivalent, independently on their name.
78
+ """
@@ -74,6 +74,9 @@ def simplify_pyproject_toml(
74
74
  return original_toml_string
75
75
 
76
76
  # Use a single platform (or skip, if not set)
77
+ # Note: we look both for `[workspace.platforms]` and `[project.platforms]`,
78
+ # even though `[project]` is deprecated as of
79
+ # https://pixi.sh/dev/CHANGELOG/#0570-2025-10-20.
77
80
  try:
78
81
  pixi_data["workspace"]["platforms"] = [pixi_platform]
79
82
  except KeyError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractal-server
3
- Version: 2.17.0a8
3
+ Version: 2.17.0a10
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License-Expression: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- fractal_server/__init__.py,sha256=BC6X5ShipZaJdAYWlIFWgdo5G_eS3ZXxSgaZP3AQsdU,25
1
+ fractal_server/__init__.py,sha256=G5BYCg1etHirfVvA4-6BLaLisXMafnQOALbQLrNgQAU,26
2
2
  fractal_server/__main__.py,sha256=68FlTuST3zbzVofFI8JSYsSBrBQ07Bv3Mu3PsZX9Fw0,11423
3
3
  fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
4
4
  fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,9 +29,9 @@ fractal_server/app/routes/admin/v2/impersonate.py,sha256=ictDjuvBr3iLv3YtwkVRMNQ
29
29
  fractal_server/app/routes/admin/v2/job.py,sha256=sFgMbOtUCIJ-ri6YD3ZWP7XETZZDQsLqPfT1kaH9RHQ,8577
30
30
  fractal_server/app/routes/admin/v2/profile.py,sha256=0Y_1Qv-BA6cHVrxPTDDBOpttpfuJN8g1FqFlG6JiOD8,3164
31
31
  fractal_server/app/routes/admin/v2/project.py,sha256=rRq7ZDngr_29skASnte1xfycZCjK-WPdeTf7siBXiCU,1182
32
- fractal_server/app/routes/admin/v2/resource.py,sha256=X5ifGY3VEVeGqtixMorh5PT6m5EHS3yg6kzzlFWmlmo,6288
33
- fractal_server/app/routes/admin/v2/task.py,sha256=aACI0RXVODclIqfaFYyQ3ncB8YCcd36qy3a0vFGnsfo,4621
34
- fractal_server/app/routes/admin/v2/task_group.py,sha256=RV697PI79X9w5zEKQTlQplAsp-vDS4fGyXwqsHc5eY0,6210
32
+ fractal_server/app/routes/admin/v2/resource.py,sha256=eLK3PxvpibwQgVfgpMb_CdqkiB7hz8-RtPqqtP9ujz8,6310
33
+ fractal_server/app/routes/admin/v2/task.py,sha256=9MMUI2PnyHQx08Xmt93O5rM60C_tlic27mP6t7ljpYo,4655
34
+ fractal_server/app/routes/admin/v2/task_group.py,sha256=EDY9oliXq_xYVJ2HgRuE4-5MbL85j-y4LbWwupZxy38,6249
35
35
  fractal_server/app/routes/admin/v2/task_group_lifecycle.py,sha256=W7LjIBAheyjrn0fEz0SsWINqcZK5HMB5GRGMjPrc6a4,9994
36
36
  fractal_server/app/routes/api/__init__.py,sha256=ewprevw6hZ0FWM-GPHoQZU0w-yfItqLeQT-Jr_Nbjnw,1658
37
37
  fractal_server/app/routes/api/v2/__init__.py,sha256=D3sRRsqkmZO6kBxUjg40q0aRDsnuXI4sOOfn0xF9JsM,2820
@@ -39,8 +39,8 @@ fractal_server/app/routes/api/v2/_aux_functions.py,sha256=0OC5ydaX-XpscMRAsqiE03
39
39
  fractal_server/app/routes/api/v2/_aux_functions_history.py,sha256=PXsqMQ3sfkABqAMI7v1_VAzUEDF_-kvaZyyhEicqsCw,4431
40
40
  fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py,sha256=5gNt35rYR8sHG2f1N8coQbOJacYIRJ5zUrmMEXcs2LQ,8585
41
41
  fractal_server/app/routes/api/v2/_aux_functions_task_version_update.py,sha256=PKjV7r8YsPRXoNiVSnOK4KBYVV3l_Yb_ZPrqAkMkXrQ,1182
42
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py,sha256=IJ_q_2x3ar1WM8u_3LM2UWdRERZU5g_N2nfQjW9Ujlc,13542
43
- fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py,sha256=8x1_q9FyCzItnPmdSdLQuwUTy4B9xCsXscp97_lJcpM,4635
42
+ fractal_server/app/routes/api/v2/_aux_functions_tasks.py,sha256=63CFMe1QZKRPmmU1xk1qZ_R6A1VANCxAlUBTGDrE0GI,13674
43
+ fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py,sha256=492tLRUWTxsAZuhkZUt9PmJtKNLIhkAqKAa-LDtwG_Y,4893
44
44
  fractal_server/app/routes/api/v2/dataset.py,sha256=HhRrReo_isFJFMaUhQfxEQkLj0_lXNfGblzwpLG81VU,8623
45
45
  fractal_server/app/routes/api/v2/history.py,sha256=T-7GDLZ_x4cp9PMEozJoEFt4cGFYPDqv9-akYmN2JSU,17150
46
46
  fractal_server/app/routes/api/v2/images.py,sha256=_R-F1qrsFv3PPukrwXSa-5swNS4kNghY0DTrxjLC_7E,7897
@@ -49,25 +49,25 @@ fractal_server/app/routes/api/v2/pre_submission_checks.py,sha256=DEnlFj6PMNMy9Dr
49
49
  fractal_server/app/routes/api/v2/project.py,sha256=PkRR1lQayBvY8uEOMherXpvv_YwIMwxWzVMjC5nFfm4,4857
50
50
  fractal_server/app/routes/api/v2/status_legacy.py,sha256=vZA4AYMehXbNFgekl4j6p8idETC3IylQQL64CmFCr98,6330
51
51
  fractal_server/app/routes/api/v2/submit.py,sha256=o0r58Dc6A1BHOUPp2OK7PKEHc4dIqnp3-kXBWnX8KsY,9373
52
- fractal_server/app/routes/api/v2/task.py,sha256=88cMBcUL_QQ25GvwAEVSiyncnJ7nZ1EX6PlLNGsgNfU,7485
53
- fractal_server/app/routes/api/v2/task_collection.py,sha256=03LXiSde7ODVsfARDdgSXWVDYMao2zce-2lqQHelF1M,12196
52
+ fractal_server/app/routes/api/v2/task.py,sha256=HIQRA7lAW41P89eveCGmUI2HERMakxOw5is-F-j-fco,7519
53
+ fractal_server/app/routes/api/v2/task_collection.py,sha256=wgY5-sry8Dg9IbLyzjxur8iqoe9ALs2zNK0bmNtO8Co,12226
54
54
  fractal_server/app/routes/api/v2/task_collection_custom.py,sha256=WjByW-raK-HU1IR02JNc39XMjkRftXyUSHwHrgnZFBw,6924
55
- fractal_server/app/routes/api/v2/task_collection_pixi.py,sha256=oSSDyhdrgE71nXjIPPA2et9LlgEKkxIBlpBkrRuEuns,7158
56
- fractal_server/app/routes/api/v2/task_group.py,sha256=_M_munannm0wMmLdOwo-qATqrl0Vu_TsrpLb-2554fs,8104
55
+ fractal_server/app/routes/api/v2/task_collection_pixi.py,sha256=-HlgVzYxZb0Ysk6fkHbhkiVvVtQxmvli1kW03PTVaVo,7191
56
+ fractal_server/app/routes/api/v2/task_group.py,sha256=7eGVim9D6AE4synpxAYMijyHIC1HTSBojY08MiuqMBg,8349
57
57
  fractal_server/app/routes/api/v2/task_group_lifecycle.py,sha256=ZSn8Ibjwkp89v87OPBquULPc9xsdpBfRvV1PiQdiicw,10593
58
58
  fractal_server/app/routes/api/v2/task_version_update.py,sha256=JO5xggiRqay4zNlbjlRa26ygMEue2klSFPyLrXS215k,8254
59
59
  fractal_server/app/routes/api/v2/workflow.py,sha256=szb6t9OWOjr0CwboF0fJUyO5jUPj_HRpf5N0CDuHkqc,10755
60
- fractal_server/app/routes/api/v2/workflow_import.py,sha256=iFy8jPuMMt1DpIcs5iJc5Gzcq-wbMtnk05NdU1y5nkQ,9095
60
+ fractal_server/app/routes/api/v2/workflow_import.py,sha256=cEs1bt5urKIbaC8PjnUwYAaPK3kGqupXpUdgwEXM0Ss,9445
61
61
  fractal_server/app/routes/api/v2/workflowtask.py,sha256=-V7WjTfYb2L7i3_dQ0Am_ydqepdqs-j3BY67_5iZPfU,7960
62
62
  fractal_server/app/routes/auth/__init__.py,sha256=Y-RQMwY5V25ZVgyYYoFIXvEDQgm6PGTDgxH755oa_NM,2358
63
- fractal_server/app/routes/auth/_aux_auth.py,sha256=fyGxBVb6yrVrsE7-2tTyiJ7orb9Jzkg36m_q-rP7Zs8,4868
64
- fractal_server/app/routes/auth/current_user.py,sha256=BsM_lm9iNiaL8_iMY8NZnfgSXhzEY8xgkQUgtC40h8o,5517
65
- fractal_server/app/routes/auth/group.py,sha256=wZnqzIOZHpoUW2Yp6dkAMRqIiM_otVdG4fZuBYmLocA,6919
63
+ fractal_server/app/routes/auth/_aux_auth.py,sha256=10eQlLWCsuwmkGiZB3eSR9JhnU4db67wsWRxu8arKqc,5435
64
+ fractal_server/app/routes/auth/current_user.py,sha256=xYun8dn9Ie3mzTczq1BC7HfIjPpj3x8QOtJ1H6Yb_GM,5585
65
+ fractal_server/app/routes/auth/group.py,sha256=JfQKmmafgVfsPmuVcA4wGs5qKwq9Xx4f-m4uwIyY3-Y,7291
66
66
  fractal_server/app/routes/auth/login.py,sha256=tSu6OBLOieoBtMZB4JkBAdEgH2Y8KqPGSbwy7NIypIo,566
67
67
  fractal_server/app/routes/auth/oauth.py,sha256=5BTEKb7Cb6ASVmgiPb5kVbHcBg0rF8Iqho0aJZd7PPQ,2730
68
68
  fractal_server/app/routes/auth/register.py,sha256=Ola-lNdYYEOK8Wh0Q-TFKwIJ25e6UswlUojJ8QGBCfc,581
69
69
  fractal_server/app/routes/auth/router.py,sha256=-E87A8h2UvcLucy5xjzKiWbXHVKcqxUmmZGeV_utEzA,598
70
- fractal_server/app/routes/auth/users.py,sha256=Gjj3_W4Zu1RZJv0r578wPJrbicMmiSELmJGJDeStyus,6878
70
+ fractal_server/app/routes/auth/users.py,sha256=xUvzH_hIwOOSU5QiJyJmU1NRZHkTcypjTgSbuTm8A5I,7060
71
71
  fractal_server/app/routes/aux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  fractal_server/app/routes/aux/_job.py,sha256=nqqdcW5B7fL_PbvHf57_TcifjUfcMgl04tKNvG2sV1U,628
73
73
  fractal_server/app/routes/aux/_runner.py,sha256=SDzI7glEfkW_XecWFuRQitbSWSvAPegI-J5c7i5d0_w,957
@@ -75,7 +75,7 @@ fractal_server/app/routes/aux/validate_user_profile.py,sha256=fGqJDdAFkbQoEIjqZ5
75
75
  fractal_server/app/routes/pagination.py,sha256=IGy8Ll5lYr6ENYE18h3huH5s0GMX1DCs5VdCi6j1cdk,1174
76
76
  fractal_server/app/schemas/__init__.py,sha256=VIWJCaqokte3OljDLX00o-EC2d12rFoPb5HOLKQI94Y,86
77
77
  fractal_server/app/schemas/user.py,sha256=MGggiWGqQV97426IzoaKNIkqHUMEuadW7BbJgom6tB8,2898
78
- fractal_server/app/schemas/user_group.py,sha256=x3-kqbo0q2wTP7QI0iZ7PU_9Dr957UYrFMKqS7BXLhE,1425
78
+ fractal_server/app/schemas/user_group.py,sha256=uTTOVGoy89SxVDpJumjqOEWxqXWR41MNOTBDCyNxEDA,1478
79
79
  fractal_server/app/schemas/v2/__init__.py,sha256=XQex5ojpELgO0xvq2l-Y8oV85ZgM3GBj6lrGGctPb1g,3729
80
80
  fractal_server/app/schemas/v2/accounting.py,sha256=C_ekrYQwhi7PtrxxB07oVAG2y1NLigXjYwyp4E38fao,396
81
81
  fractal_server/app/schemas/v2/dataset.py,sha256=NKCjBwGBC7mPiSlXktZAcleJsvlLY6KfNKw7Wx4Zfqk,1728
@@ -92,14 +92,14 @@ fractal_server/app/schemas/v2/task_collection.py,sha256=BzHQXq2_zLZTbigWauOR5Zi-
92
92
  fractal_server/app/schemas/v2/task_group.py,sha256=4hNZUXnWYSozpLXR3JqBvGzfZBG2TbjqydckHHu2Aq0,3506
93
93
  fractal_server/app/schemas/v2/workflow.py,sha256=L-dW6SzCH_VNoH6ENip44lTgGGqVYHHBk_3PtM-Ooy8,1772
94
94
  fractal_server/app/schemas/v2/workflowtask.py,sha256=6eweAMyziwaoMT-7R1fVJYunIeZKzT0-7fAVgPO_FEc,3639
95
- fractal_server/app/security/__init__.py,sha256=gtfJkwdXTm_F38sFgb5LH3AcdvKs42fOUJa8jo8Cmbw,17484
95
+ fractal_server/app/security/__init__.py,sha256=k-La8Da89C1hSUGsiidrWo6Az4u6dbe5PzN1Ctt1t34,18394
96
96
  fractal_server/app/security/signup_email.py,sha256=kphjq6TAygvPpYpg95QJWefyqmzdVrGz7fyRMctUJWE,1982
97
97
  fractal_server/app/shutdown.py,sha256=ViSNJyXWU_iWPSDOOMGNh_iQdUFrdPh_jvf8vVKLpAo,1950
98
98
  fractal_server/config/__init__.py,sha256=ZCmroNB50sUxJiFtkW0a4fFtmfyPnL4LWhtKY5FbQfg,737
99
99
  fractal_server/config/_data.py,sha256=9Jyt83yrSsr_0_9ANWDAXz88_jjyFlcB5VWJGXq8aUY,2311
100
100
  fractal_server/config/_database.py,sha256=YOBi3xuJno5wLGw1hKsjLm-bftaxVWiBNIQWVTMX3Ag,1661
101
101
  fractal_server/config/_email.py,sha256=j1QmZCyspNbD1xxkypc9Kv299tU3vTO1AqDFJ8-LZzQ,4201
102
- fractal_server/config/_main.py,sha256=L0sLfePAlnvEPxxtzDdY3xJRhzTAvIO5pdEoTd99byY,1605
102
+ fractal_server/config/_main.py,sha256=9v64gJsvY1oGP70_AoJMnyMIeRo7FcIg6T8NDV-p9as,1992
103
103
  fractal_server/config/_oauth.py,sha256=7J4FphGVFfVmtQycCkas6scEJQJGZUGEzQ-t2PZiqSo,1934
104
104
  fractal_server/config/_settings_config.py,sha256=tsyXQOnn9QKCFJD6hRo_dJXlQQyl70DbqgHMJoZ1xnY,144
105
105
  fractal_server/data_migrations/README.md,sha256=_3AEFvDg9YkybDqCLlFPdDmGJvr6Tw7HRI14aZ3LOIw,398
@@ -243,7 +243,7 @@ fractal_server/tasks/v2/templates/pixi_3_post_install.sh,sha256=99J8KXkNeQk9utuE
243
243
  fractal_server/tasks/v2/utils_background.py,sha256=jjWxNbHPuvAkXNIQ9Bqs67X72xHSRqmY8_BCoj0HM3E,4840
244
244
  fractal_server/tasks/v2/utils_database.py,sha256=C1td6m6ab1NdXVoT6stvVdrY_3FNck8OkRZZhn9h8ZA,1797
245
245
  fractal_server/tasks/v2/utils_package_names.py,sha256=RDg__xrvQs4ieeVzmVdMcEh95vGQYrv9Hfal-5EDBM8,2393
246
- fractal_server/tasks/v2/utils_pixi.py,sha256=tqCnxMdxs7KGWncWvk0alrdvDbX-w77P3fAot68Bqh4,3257
246
+ fractal_server/tasks/v2/utils_pixi.py,sha256=4q0inbOfJ-TWe7wFIq-f7W7-iKyd98tgNQpEkR-TE6E,3441
247
247
  fractal_server/tasks/v2/utils_python_interpreter.py,sha256=36AvrMoydr9w6Rm_7hKl5QK8zYI0KIm4Pv8WHANWwjE,658
248
248
  fractal_server/tasks/v2/utils_templates.py,sha256=L5GblhIKJwyzUbCORj1et5mh-7mG19nT5kmIpxOEj90,3489
249
249
  fractal_server/types/__init__.py,sha256=aA_8J1xXzuiLqpwO_Qf18-qzaRcYkHzevhH_T-diXWM,2026
@@ -254,8 +254,8 @@ fractal_server/types/validators/_workflow_task_arguments_validators.py,sha256=HL
254
254
  fractal_server/urls.py,sha256=QjIKAC1a46bCdiPMu3AlpgFbcv6a4l3ABcd5xz190Og,471
255
255
  fractal_server/utils.py,sha256=SYVVUuXe_nWyrJLsy7QA-KJscwc5PHEXjvsW4TK7XQI,2180
256
256
  fractal_server/zip_tools.py,sha256=H0w7wS5yE4ebj7hw1_77YQ959dl2c-L0WX6J_ro1TY4,4884
257
- fractal_server-2.17.0a8.dist-info/METADATA,sha256=pq54GpIDEoMvbVhOdhAZFeogzh1wQNJ0WcbSi0DsdSw,4226
258
- fractal_server-2.17.0a8.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
259
- fractal_server-2.17.0a8.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
260
- fractal_server-2.17.0a8.dist-info/licenses/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
261
- fractal_server-2.17.0a8.dist-info/RECORD,,
257
+ fractal_server-2.17.0a10.dist-info/METADATA,sha256=-hVkYe_goWyUvm96LujeOSDCXsmkYPVPlojm9jxLHGs,4227
258
+ fractal_server-2.17.0a10.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
259
+ fractal_server-2.17.0a10.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
260
+ fractal_server-2.17.0a10.dist-info/licenses/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
261
+ fractal_server-2.17.0a10.dist-info/RECORD,,