fractal-server 2.9.0a9__py3-none-any.whl → 2.9.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.9.0a9"
1
+ __VERSION__ = "2.9.0a10"
@@ -7,6 +7,7 @@ from fastapi import status
7
7
  from pydantic import BaseModel
8
8
  from pydantic import EmailStr
9
9
  from pydantic import Field
10
+ from sqlmodel import func
10
11
  from sqlmodel import select
11
12
 
12
13
  from fractal_server.app.db import AsyncSession
@@ -60,6 +61,9 @@ async def query_tasks(
60
61
  version: Optional[str] = None,
61
62
  name: Optional[str] = None,
62
63
  max_number_of_results: int = 25,
64
+ category: Optional[str] = None,
65
+ modality: Optional[str] = None,
66
+ author: Optional[str] = None,
63
67
  user: UserOAuth = Depends(current_active_superuser),
64
68
  db: AsyncSession = Depends(get_async_db),
65
69
  ) -> list[TaskV2Info]:
@@ -74,6 +78,9 @@ async def query_tasks(
74
78
  version: If not `None`, query for matching `task.version`.
75
79
  name: If not `None`, query for contained case insensitive `task.name`.
76
80
  max_number_of_results: The maximum length of the response.
81
+ category:
82
+ modality:
83
+ author:
77
84
  """
78
85
 
79
86
  stm = select(TaskV2)
@@ -86,6 +93,12 @@ async def query_tasks(
86
93
  stm = stm.where(TaskV2.version == version)
87
94
  if name is not None:
88
95
  stm = stm.where(TaskV2.name.icontains(name))
96
+ if category is not None:
97
+ stm = stm.where(func.lower(TaskV2.category) == category.lower())
98
+ if modality is not None:
99
+ stm = stm.where(func.lower(TaskV2.modality) == modality.lower())
100
+ if author is not None:
101
+ stm = stm.where(TaskV2.authors.icontains(author))
89
102
 
90
103
  res = await db.execute(stm)
91
104
  task_list = res.scalars().all()
@@ -6,14 +6,12 @@ from fastapi import Depends
6
6
  from fastapi import HTTPException
7
7
  from fastapi import Response
8
8
  from fastapi import status
9
- from sqlalchemy.exc import IntegrityError
10
9
  from sqlalchemy.ext.asyncio import AsyncSession
11
- from sqlmodel import col
12
- from sqlmodel import func
13
10
  from sqlmodel import select
14
11
 
15
12
  from . import current_active_superuser
16
13
  from ._aux_auth import _get_single_usergroup_with_user_ids
14
+ from ._aux_auth import _user_or_404
17
15
  from ._aux_auth import _usergroup_or_404
18
16
  from fractal_server.app.db import get_async_db
19
17
  from fractal_server.app.models import LinkUserGroup
@@ -126,42 +124,6 @@ async def update_single_group(
126
124
 
127
125
  group = await _usergroup_or_404(group_id, db)
128
126
 
129
- # Check that all required users exist
130
- # Note: The reason for introducing `col` is as in
131
- # https://sqlmodel.tiangolo.com/tutorial/where/#type-annotations-and-errors,
132
- stm = select(func.count()).where(
133
- col(UserOAuth.id).in_(group_update.new_user_ids)
134
- )
135
- res = await db.execute(stm)
136
- number_matching_users = res.scalar()
137
- if number_matching_users != len(group_update.new_user_ids):
138
- raise HTTPException(
139
- status_code=status.HTTP_404_NOT_FOUND,
140
- detail=(
141
- f"Not all requested users (IDs {group_update.new_user_ids}) "
142
- "exist."
143
- ),
144
- )
145
-
146
- # Add new users to existing group
147
- for user_id in group_update.new_user_ids:
148
- link = LinkUserGroup(user_id=user_id, group_id=group_id)
149
- db.add(link)
150
- try:
151
- await db.commit()
152
- except IntegrityError as e:
153
- error_msg = (
154
- f"Cannot link users with IDs {group_update.new_user_ids} "
155
- f"to group {group_id}. "
156
- "Likely reason: one of these links already exists.\n"
157
- f"Original error: {str(e)}"
158
- )
159
- logger.info(error_msg)
160
- raise HTTPException(
161
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
162
- detail=error_msg,
163
- )
164
-
165
127
  # Patch `viewer_paths`
166
128
  if group_update.viewer_paths is not None:
167
129
  group.viewer_paths = group_update.viewer_paths
@@ -239,3 +201,49 @@ async def patch_user_settings_bulk(
239
201
  await db.commit()
240
202
 
241
203
  return Response(status_code=status.HTTP_200_OK)
204
+
205
+
206
+ @router_group.post("/group/{group_id}/add-user/{user_id}/", status_code=200)
207
+ async def add_user_to_group(
208
+ group_id: int,
209
+ user_id: int,
210
+ superuser: UserOAuth = Depends(current_active_superuser),
211
+ db: AsyncSession = Depends(get_async_db),
212
+ ) -> UserGroupRead:
213
+ await _usergroup_or_404(group_id, db)
214
+ user = await _user_or_404(user_id, db)
215
+ link = await db.get(LinkUserGroup, (group_id, user_id))
216
+ if link is None:
217
+ db.add(LinkUserGroup(group_id=group_id, user_id=user_id))
218
+ await db.commit()
219
+ else:
220
+ raise HTTPException(
221
+ status_code=422,
222
+ detail=(
223
+ f"User '{user.email}' is already a member of group {group_id}."
224
+ ),
225
+ )
226
+ group = await _get_single_usergroup_with_user_ids(group_id=group_id, db=db)
227
+ return group
228
+
229
+
230
+ @router_group.post("/group/{group_id}/remove-user/{user_id}/", status_code=200)
231
+ async def remove_user_from_group(
232
+ group_id: int,
233
+ user_id: int,
234
+ superuser: UserOAuth = Depends(current_active_superuser),
235
+ db: AsyncSession = Depends(get_async_db),
236
+ ) -> UserGroupRead:
237
+ await _usergroup_or_404(group_id, db)
238
+ user = await _user_or_404(user_id, db)
239
+ link = await db.get(LinkUserGroup, (group_id, user_id))
240
+ if link is None:
241
+ raise HTTPException(
242
+ status_code=422,
243
+ detail=f"User '{user.email}' is not a member of group {group_id}.",
244
+ )
245
+ else:
246
+ await db.delete(link)
247
+ await db.commit()
248
+ group = await _get_single_usergroup_with_user_ids(group_id=group_id, db=db)
249
+ return group
@@ -59,21 +59,10 @@ class UserGroupCreate(BaseModel, extra=Extra.forbid):
59
59
  class UserGroupUpdate(BaseModel, extra=Extra.forbid):
60
60
  """
61
61
  Schema for `UserGroup` update
62
-
63
- NOTE: `new_user_ids` does not correspond to a column of the `UserGroup`
64
- table, but it is rather used to create new `LinkUserGroup` rows.
65
-
66
- Attributes:
67
- new_user_ids: IDs of groups to be associated to user.
68
62
  """
69
63
 
70
- new_user_ids: list[int] = Field(default_factory=list)
71
64
  viewer_paths: Optional[list[str]] = None
72
65
 
73
- _val_unique = validator("new_user_ids", allow_reuse=True)(
74
- val_unique_list("new_user_ids")
75
- )
76
-
77
66
  @validator("viewer_paths")
78
67
  def viewer_paths_validator(cls, value):
79
68
  for i, path in enumerate(value):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.9.0a9
3
+ Version: 2.9.0a10
4
4
  Summary: Server component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -1,4 +1,4 @@
1
- fractal_server/__init__.py,sha256=AgkJcJmAplHlQOvkvgp6iM_-dHeN1ajiKqWDu3YtZwc,24
1
+ fractal_server/__init__.py,sha256=bOb7y0ERfwPyi1oCYCLL0TUUUEB3QNEldOxpWFB5zGo,25
2
2
  fractal_server/__main__.py,sha256=dEkCfzLLQrIlxsGC-HBfoR-RBMWnJDgNrxYTyzmE9c0,6146
3
3
  fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
4
4
  fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,7 +29,7 @@ fractal_server/app/routes/admin/v1.py,sha256=ggJZMeKhRijfVe2h2VzfIcpR15FqkKImANh
29
29
  fractal_server/app/routes/admin/v2/__init__.py,sha256=KYrw0COmmMuIMp7c6YcYRXah4tEYplCWeROnPK1VTeg,681
30
30
  fractal_server/app/routes/admin/v2/job.py,sha256=cbkFIRIIXaWmNsUFI7RAu8HpQ0mWn_bgoxtvWZxr-IA,7624
31
31
  fractal_server/app/routes/admin/v2/project.py,sha256=luy-yiGX1JYTdPm1hpIdDUUqPm8xHuipLy9k2X6zu74,1223
32
- fractal_server/app/routes/admin/v2/task.py,sha256=Y0eujBgGhVapNXfW9azDxw4EBzLmEmCdh70y1RNQcb0,3895
32
+ fractal_server/app/routes/admin/v2/task.py,sha256=gShC2EAOYa0qTB69EXTDXz5Y375QoarOLv9T9vfntAE,4368
33
33
  fractal_server/app/routes/admin/v2/task_group.py,sha256=DncrOAB4q-v3BAmxg35m4EohleriW_FLGE5gpW_Or08,8120
34
34
  fractal_server/app/routes/admin/v2/task_group_lifecycle.py,sha256=0e0ZJ_k75TVHaT2o8Xk33DPDSgh-eBhZf-y4y7t-Adg,9429
35
35
  fractal_server/app/routes/api/__init__.py,sha256=2IDheFi0OFdsUg7nbUiyahqybvpgXqeHUXIL2QtWrQQ,641
@@ -63,7 +63,7 @@ fractal_server/app/routes/api/v2/workflowtask.py,sha256=ciHTwXXFiFnMF7ZpJ3Xs0q6Y
63
63
  fractal_server/app/routes/auth/__init__.py,sha256=fao6CS0WiAjHDTvBzgBVV_bSXFpEAeDBF6Z6q7rRkPc,1658
64
64
  fractal_server/app/routes/auth/_aux_auth.py,sha256=ifkNocTYatBSMYGwiR14qohmvR9SfMldceiEj6uJBrU,4783
65
65
  fractal_server/app/routes/auth/current_user.py,sha256=I3aVY5etWAJ_SH6t65Mj5TjvB2X8sAGuu1KG7FxLyPU,5883
66
- fractal_server/app/routes/auth/group.py,sha256=dSS7r8J2cejZ6sKnOWAPSDKynxD9VyBNtqDbFpySzIU,7489
66
+ fractal_server/app/routes/auth/group.py,sha256=cS9I6pCIWGbOWc3gUBYmQq6yjFYzm6rVQDukWF_9L90,7721
67
67
  fractal_server/app/routes/auth/login.py,sha256=tSu6OBLOieoBtMZB4JkBAdEgH2Y8KqPGSbwy7NIypIo,566
68
68
  fractal_server/app/routes/auth/oauth.py,sha256=AnFHbjqL2AgBX3eksI931xD6RTtmbciHBEuGf9YJLjU,1895
69
69
  fractal_server/app/routes/auth/register.py,sha256=DlHq79iOvGd_gt2v9uwtsqIKeO6i_GKaW59VIkllPqY,587
@@ -136,7 +136,7 @@ fractal_server/app/runner/versions.py,sha256=dSaPRWqmFPHjg20kTCHmi_dmGNcCETflDtD
136
136
  fractal_server/app/schemas/__init__.py,sha256=stURAU_t3AOBaH0HSUbV-GKhlPKngnnIMoqWc3orFyI,135
137
137
  fractal_server/app/schemas/_validators.py,sha256=T5EswIJAJRvawfzqWtPcN2INAfiBXyE4m0iwQm4ht-0,3149
138
138
  fractal_server/app/schemas/user.py,sha256=aUD8YAcfYTEO06TEUoTx4heVrXFiX7E2Mb8D2--4FsA,2130
139
- fractal_server/app/schemas/user_group.py,sha256=YwJvYgj-PI66LWy38CEd_FIZPsBV1_2N5zJPGFcFvBw,2143
139
+ fractal_server/app/schemas/user_group.py,sha256=t30Kd07PY43G_AqFDb8vjdInTeLeU9WvFZDx8fVLPSI,1750
140
140
  fractal_server/app/schemas/user_settings.py,sha256=TalISeEfCrtN8LgqbLx1Q8ZPoeiZnbksg5NYAVzkIqY,3527
141
141
  fractal_server/app/schemas/v1/__init__.py,sha256=CrBGgBhoemCvmZ70ZUchM-jfVAICnoa7AjZBAtL2UB0,1852
142
142
  fractal_server/app/schemas/v1/applyworkflow.py,sha256=dYArxQAOBdUIEXX_Ejz8b9fBhEYu1nMm6b_Z6_P6TgA,4052
@@ -238,8 +238,8 @@ fractal_server/tasks/v2/utils_templates.py,sha256=C5WLuY3uGG2s53OEL-__H35-fmSlgu
238
238
  fractal_server/urls.py,sha256=5o_qq7PzKKbwq12NHSQZDmDitn5RAOeQ4xufu-2v9Zk,448
239
239
  fractal_server/utils.py,sha256=utvmBx8K9I8hRWFquxna2pBaOqe0JifDL_NVPmihEJI,3525
240
240
  fractal_server/zip_tools.py,sha256=GjDgo_sf6V_DDg6wWeBlZu5zypIxycn_l257p_YVKGc,4876
241
- fractal_server-2.9.0a9.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
242
- fractal_server-2.9.0a9.dist-info/METADATA,sha256=kVjqPsTd7RPiIlTSbj8CzDM6dZEKzvo23ofH6ylpl2E,4585
243
- fractal_server-2.9.0a9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
244
- fractal_server-2.9.0a9.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
245
- fractal_server-2.9.0a9.dist-info/RECORD,,
241
+ fractal_server-2.9.0a10.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
242
+ fractal_server-2.9.0a10.dist-info/METADATA,sha256=m2WwGM-wSD_5ffYqaGCVERw4RZ7yhM7VtxSdlGt3xGQ,4586
243
+ fractal_server-2.9.0a10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
244
+ fractal_server-2.9.0a10.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
245
+ fractal_server-2.9.0a10.dist-info/RECORD,,