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.
- fractal_server/__init__.py +1 -1
- fractal_server/app/routes/admin/v2/task.py +13 -0
- fractal_server/app/routes/auth/group.py +47 -39
- fractal_server/app/schemas/user_group.py +0 -11
- {fractal_server-2.9.0a9.dist-info → fractal_server-2.9.0a10.dist-info}/METADATA +1 -1
- {fractal_server-2.9.0a9.dist-info → fractal_server-2.9.0a10.dist-info}/RECORD +9 -9
- {fractal_server-2.9.0a9.dist-info → fractal_server-2.9.0a10.dist-info}/LICENSE +0 -0
- {fractal_server-2.9.0a9.dist-info → fractal_server-2.9.0a10.dist-info}/WHEEL +0 -0
- {fractal_server-2.9.0a9.dist-info → fractal_server-2.9.0a10.dist-info}/entry_points.txt +0 -0
fractal_server/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__VERSION__ = "2.9.
|
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,4 +1,4 @@
|
|
1
|
-
fractal_server/__init__.py,sha256=
|
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=
|
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=
|
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=
|
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.
|
242
|
-
fractal_server-2.9.
|
243
|
-
fractal_server-2.9.
|
244
|
-
fractal_server-2.9.
|
245
|
-
fractal_server-2.9.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|