desdeo 2.0.0__py3-none-any.whl → 2.1.0__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.
- desdeo/adm/ADMAfsar.py +551 -0
- desdeo/adm/ADMChen.py +414 -0
- desdeo/adm/BaseADM.py +119 -0
- desdeo/adm/__init__.py +11 -0
- desdeo/api/__init__.py +6 -6
- desdeo/api/app.py +38 -28
- desdeo/api/config.py +65 -44
- desdeo/api/config.toml +23 -12
- desdeo/api/db.py +10 -8
- desdeo/api/db_init.py +12 -6
- desdeo/api/models/__init__.py +220 -20
- desdeo/api/models/archive.py +16 -27
- desdeo/api/models/emo.py +128 -0
- desdeo/api/models/enautilus.py +69 -0
- desdeo/api/models/gdm/gdm_aggregate.py +139 -0
- desdeo/api/models/gdm/gdm_base.py +69 -0
- desdeo/api/models/gdm/gdm_score_bands.py +114 -0
- desdeo/api/models/gdm/gnimbus.py +138 -0
- desdeo/api/models/generic.py +104 -0
- desdeo/api/models/generic_states.py +401 -0
- desdeo/api/models/nimbus.py +158 -0
- desdeo/api/models/preference.py +44 -6
- desdeo/api/models/problem.py +274 -64
- desdeo/api/models/session.py +4 -1
- desdeo/api/models/state.py +419 -52
- desdeo/api/models/user.py +7 -6
- desdeo/api/models/utopia.py +25 -0
- desdeo/api/routers/_EMO.backup +309 -0
- desdeo/api/routers/_NIMBUS.py +6 -3
- desdeo/api/routers/emo.py +497 -0
- desdeo/api/routers/enautilus.py +237 -0
- desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
- desdeo/api/routers/gdm/gdm_base.py +420 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
- desdeo/api/routers/generic.py +233 -0
- desdeo/api/routers/nimbus.py +705 -0
- desdeo/api/routers/problem.py +201 -4
- desdeo/api/routers/reference_point_method.py +20 -44
- desdeo/api/routers/session.py +50 -26
- desdeo/api/routers/user_authentication.py +180 -26
- desdeo/api/routers/utils.py +187 -0
- desdeo/api/routers/utopia.py +230 -0
- desdeo/api/schema.py +10 -4
- desdeo/api/tests/conftest.py +94 -2
- desdeo/api/tests/test_enautilus.py +330 -0
- desdeo/api/tests/test_models.py +550 -72
- desdeo/api/tests/test_routes.py +902 -43
- desdeo/api/utils/_database.py +263 -0
- desdeo/api/utils/database.py +28 -266
- desdeo/api/utils/emo_database.py +40 -0
- desdeo/core.py +7 -0
- desdeo/emo/__init__.py +154 -24
- desdeo/emo/hooks/archivers.py +18 -2
- desdeo/emo/methods/EAs.py +128 -5
- desdeo/emo/methods/bases.py +9 -56
- desdeo/emo/methods/templates.py +111 -0
- desdeo/emo/operators/crossover.py +544 -42
- desdeo/emo/operators/evaluator.py +10 -14
- desdeo/emo/operators/generator.py +127 -24
- desdeo/emo/operators/mutation.py +212 -41
- desdeo/emo/operators/scalar_selection.py +202 -0
- desdeo/emo/operators/selection.py +956 -214
- desdeo/emo/operators/termination.py +124 -16
- desdeo/emo/options/__init__.py +108 -0
- desdeo/emo/options/algorithms.py +435 -0
- desdeo/emo/options/crossover.py +164 -0
- desdeo/emo/options/generator.py +131 -0
- desdeo/emo/options/mutation.py +260 -0
- desdeo/emo/options/repair.py +61 -0
- desdeo/emo/options/scalar_selection.py +66 -0
- desdeo/emo/options/selection.py +127 -0
- desdeo/emo/options/templates.py +383 -0
- desdeo/emo/options/termination.py +143 -0
- desdeo/gdm/__init__.py +22 -0
- desdeo/gdm/gdmtools.py +45 -0
- desdeo/gdm/score_bands.py +114 -0
- desdeo/gdm/voting_rules.py +50 -0
- desdeo/mcdm/__init__.py +23 -1
- desdeo/mcdm/enautilus.py +338 -0
- desdeo/mcdm/gnimbus.py +484 -0
- desdeo/mcdm/nautilus_navigator.py +7 -6
- desdeo/mcdm/reference_point_method.py +70 -0
- desdeo/problem/__init__.py +5 -1
- desdeo/problem/external/__init__.py +18 -0
- desdeo/problem/external/core.py +356 -0
- desdeo/problem/external/pymoo_provider.py +266 -0
- desdeo/problem/external/runtime.py +44 -0
- desdeo/problem/infix_parser.py +2 -2
- desdeo/problem/pyomo_evaluator.py +25 -6
- desdeo/problem/schema.py +69 -48
- desdeo/problem/simulator_evaluator.py +65 -15
- desdeo/problem/testproblems/__init__.py +26 -11
- desdeo/problem/testproblems/benchmarks_server.py +120 -0
- desdeo/problem/testproblems/cake_problem.py +185 -0
- desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
- desdeo/problem/testproblems/forest_problem.py +77 -69
- desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
- desdeo/problem/testproblems/{river_pollution_problem.py → river_pollution_problems.py} +28 -22
- desdeo/problem/testproblems/single_objective.py +289 -0
- desdeo/problem/testproblems/zdt_problem.py +4 -1
- desdeo/tools/__init__.py +39 -21
- desdeo/tools/desc_gen.py +22 -0
- desdeo/tools/generics.py +22 -2
- desdeo/tools/group_scalarization.py +3090 -0
- desdeo/tools/indicators_binary.py +107 -1
- desdeo/tools/indicators_unary.py +3 -16
- desdeo/tools/message.py +33 -2
- desdeo/tools/non_dominated_sorting.py +4 -3
- desdeo/tools/patterns.py +9 -7
- desdeo/tools/pyomo_solver_interfaces.py +48 -35
- desdeo/tools/reference_vectors.py +118 -351
- desdeo/tools/scalarization.py +340 -1413
- desdeo/tools/score_bands.py +491 -328
- desdeo/tools/utils.py +117 -49
- desdeo/tools/visualizations.py +67 -0
- desdeo/utopia_stuff/utopia_problem.py +1 -1
- desdeo/utopia_stuff/utopia_problem_old.py +1 -1
- {desdeo-2.0.0.dist-info → desdeo-2.1.0.dist-info}/METADATA +46 -28
- desdeo-2.1.0.dist-info/RECORD +180 -0
- {desdeo-2.0.0.dist-info → desdeo-2.1.0.dist-info}/WHEEL +1 -1
- desdeo-2.0.0.dist-info/RECORD +0 -120
- /desdeo/api/utils/{logger.py → _logger.py} +0 -0
- {desdeo-2.0.0.dist-info → desdeo-2.1.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Necessary routers for GDM Score Bands.
|
|
2
|
+
|
|
3
|
+
I imagine these as simple interfaces to the GDMScoreBandsManager.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import polars as pl
|
|
11
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
|
12
|
+
from fastapi.responses import JSONResponse
|
|
13
|
+
from sqlmodel import Session, select
|
|
14
|
+
|
|
15
|
+
from desdeo.api.db import get_session
|
|
16
|
+
from desdeo.api.models import (
|
|
17
|
+
GDMSCOREBandInformation,
|
|
18
|
+
GDMSCOREBandsDecisionResponse,
|
|
19
|
+
GDMSCOREBandsHistoryResponse,
|
|
20
|
+
GDMScoreBandsInitializationRequest,
|
|
21
|
+
GDMSCOREBandsResponse,
|
|
22
|
+
GDMSCOREBandsRevertRequest,
|
|
23
|
+
GDMScoreBandsVoteRequest,
|
|
24
|
+
Group,
|
|
25
|
+
GroupInfoRequest,
|
|
26
|
+
GroupIteration,
|
|
27
|
+
User,
|
|
28
|
+
)
|
|
29
|
+
from desdeo.api.routers.gdm.gdm_aggregate import manager
|
|
30
|
+
from desdeo.api.routers.gdm.gdm_score_bands.gdm_score_bands_manager import GDMScoreBandsManager
|
|
31
|
+
from desdeo.api.routers.user_authentication import get_current_user
|
|
32
|
+
from desdeo.gdm.score_bands import SCOREBandsGDMConfig, SCOREBandsGDMResult, score_bands_gdm
|
|
33
|
+
|
|
34
|
+
logging.basicConfig(
|
|
35
|
+
stream=sys.stdout, format="[%(filename)s:%(lineno)d] %(levelname)s: %(message)s", level=logging.INFO
|
|
36
|
+
)
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
router = APIRouter(prefix="/gdm-score-bands", tags=["GDM Score Bands"])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@router.post("/vote")
|
|
43
|
+
async def vote_for_a_band(
|
|
44
|
+
request: GDMScoreBandsVoteRequest,
|
|
45
|
+
user: Annotated[User, Depends(get_current_user)],
|
|
46
|
+
session: Annotated[Session, Depends(get_session)],
|
|
47
|
+
):
|
|
48
|
+
"""Vote for a band using this endpoint.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
request (GDMScoreBandsVoteRequest): A container for the group id and the vote.
|
|
52
|
+
user (Annotated[User, Depends): the current user.
|
|
53
|
+
session (Annotated[Session, Depends): database session
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
HTTPException: If something goes wrong. It hopefully let's you know what went wrong.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
JSONResponse: A quick confirmation that vote went through.
|
|
60
|
+
"""
|
|
61
|
+
group_id = request.group_id
|
|
62
|
+
vote = request.vote
|
|
63
|
+
group = session.exec(select(Group).where(Group.id == group_id)).first()
|
|
64
|
+
if not group:
|
|
65
|
+
raise HTTPException(detail=f"Group with ID {group_id} does not exist!", status_code=status.HTTP_404_NOT_FOUND)
|
|
66
|
+
if user.id not in group.user_ids:
|
|
67
|
+
raise HTTPException(
|
|
68
|
+
detail=f"User with ID {user.id} is not part of group with ID {group.id}. Could be the owner though.",
|
|
69
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
70
|
+
)
|
|
71
|
+
try:
|
|
72
|
+
group_mgr: GDMScoreBandsManager = await manager.get_group_manager(
|
|
73
|
+
group_id=group_id, method="gdm-score-bands", db_session=session
|
|
74
|
+
)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(e)
|
|
77
|
+
raise
|
|
78
|
+
|
|
79
|
+
# This would be the better way to do things.
|
|
80
|
+
try:
|
|
81
|
+
await group_mgr.vote(user=user, group=group, voted_index=vote, session=session)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.exception("Found an error when issuing a vote for a band.")
|
|
84
|
+
raise HTTPException(
|
|
85
|
+
detail=f"Internal server error: {e}", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
86
|
+
) from e
|
|
87
|
+
|
|
88
|
+
return JSONResponse(content={"message": f"Voted for index {vote} by user with ID {user.id}"})
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@router.post("/confirm")
|
|
92
|
+
async def confirm_vote(
|
|
93
|
+
request: GroupInfoRequest,
|
|
94
|
+
user: Annotated[User, Depends(get_current_user)],
|
|
95
|
+
session: Annotated[Session, Depends(get_session)],
|
|
96
|
+
):
|
|
97
|
+
"""Confim the vote. If all confirm, the clustering and new iteration begins.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
request (GroupInfoRequest): Simple request to get the group ID.
|
|
101
|
+
user (Annotated[User, Depends): The current user.
|
|
102
|
+
session (Annotated[Session, Depends): Database session.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
HTTPException: If something goes awry. It should let you know what went wrong, though.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
JSONResponse: A simple confirmation that everything went ok and that vote went in.
|
|
109
|
+
"""
|
|
110
|
+
group_id = request.group_id
|
|
111
|
+
group = session.exec(select(Group).where(Group.id == group_id)).first()
|
|
112
|
+
if not group:
|
|
113
|
+
raise HTTPException(detail=f"Group with ID {group_id} does not exist!", status_code=status.HTTP_404_NOT_FOUND)
|
|
114
|
+
if user.id not in group.user_ids:
|
|
115
|
+
raise HTTPException(
|
|
116
|
+
detail=f"User with ID {user.id} is not part of group with ID {group.id}. Could be the owner though.",
|
|
117
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
118
|
+
)
|
|
119
|
+
group_mgr: GDMScoreBandsManager = await manager.get_group_manager(
|
|
120
|
+
group_id=group_id, method="gdm-score-bands", db_session=session
|
|
121
|
+
)
|
|
122
|
+
try:
|
|
123
|
+
await group_mgr.confirm(user=user, group=group, session=session)
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.exception("Found and error when trying to confirm a vote.")
|
|
126
|
+
raise HTTPException(
|
|
127
|
+
detail=f"Internal server error: {e}", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
128
|
+
) from e
|
|
129
|
+
|
|
130
|
+
return JSONResponse(content={"message": f"Confirmed vote and moving on for user with ID {user.id}"})
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@router.post("/get-or-initialize")
|
|
134
|
+
async def get_or_initialize(
|
|
135
|
+
request: GDMScoreBandsInitializationRequest,
|
|
136
|
+
user: Annotated[User, Depends(get_current_user)],
|
|
137
|
+
session: Annotated[Session, Depends(get_session)],
|
|
138
|
+
) -> GDMSCOREBandsHistoryResponse:
|
|
139
|
+
"""An endpoint for two things: Initializing the GDM Score Bands things and Fetching results.
|
|
140
|
+
|
|
141
|
+
If a group hasn't been initialized, initialize and then return initial clustering information.
|
|
142
|
+
If it has been initialized, just fetch the latest iteration's information (clustering, etc.)
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
request (GDMScoreBandsInitializationRequest): Request that contains necessary information for initialization.
|
|
146
|
+
user (Annotated[User, Depends): The current user.
|
|
147
|
+
session (Annotated[Session, Depends): Database session.
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
HTTPException: It'll let you know.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
GDMSCOREBandsResponse: A response containing Group id, group iter id and ScoreBandsResponse.
|
|
154
|
+
"""
|
|
155
|
+
group: Group = session.exec(select(Group).where(Group.id == request.group_id)).first()
|
|
156
|
+
if not group:
|
|
157
|
+
raise HTTPException(
|
|
158
|
+
detail=f"Group with ID {request.group_id} not found!", status_code=status.HTTP_404_NOT_FOUND
|
|
159
|
+
)
|
|
160
|
+
if group.head_iteration_id is not None:
|
|
161
|
+
# Actually, just return the newest score band data.
|
|
162
|
+
print("Group already initialized!")
|
|
163
|
+
group_iterations = session.exec(select(GroupIteration).where(GroupIteration.group_id == group.id)).all()
|
|
164
|
+
responses: list[GDMSCOREBandsResponse | GDMSCOREBandsDecisionResponse] = []
|
|
165
|
+
for giter in group_iterations:
|
|
166
|
+
match giter.info_container.method:
|
|
167
|
+
case "gdm-score-bands":
|
|
168
|
+
responses.append(
|
|
169
|
+
GDMSCOREBandsResponse(
|
|
170
|
+
group_id=group.id,
|
|
171
|
+
group_iter_id=giter.id,
|
|
172
|
+
latest_iteration=giter.info_container.score_bands_result.iteration,
|
|
173
|
+
result=giter.info_container.score_bands_result.score_bands_result,
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
case "gdm-score-bands-final":
|
|
177
|
+
responses.append(
|
|
178
|
+
GDMSCOREBandsDecisionResponse(
|
|
179
|
+
group_id=group.id, group_iter_id=giter.id, result=giter.info_container
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
return GDMSCOREBandsHistoryResponse(history=responses)
|
|
183
|
+
user_ids = group.user_ids
|
|
184
|
+
user_ids.append(group.owner_id)
|
|
185
|
+
if user.id not in user_ids:
|
|
186
|
+
raise HTTPException(
|
|
187
|
+
detail=f"User with ID {user.id} is not part of group with ID {group.id}",
|
|
188
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
189
|
+
)
|
|
190
|
+
group_mgr: GDMScoreBandsManager = await manager.get_group_manager(
|
|
191
|
+
group_id=group.id, method="gdm-score-bands", db_session=session
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
score_bands_config = SCOREBandsGDMConfig() if request.score_bands_config is None else request.score_bands_config
|
|
195
|
+
|
|
196
|
+
# initial clustering for the objectives
|
|
197
|
+
discrete_representation_obj = group_mgr.discrete_representation.objective_values
|
|
198
|
+
objs = pl.DataFrame(discrete_representation_obj)
|
|
199
|
+
result: SCOREBandsGDMResult = score_bands_gdm(data=objs, config=score_bands_config, state=None)[-1]
|
|
200
|
+
|
|
201
|
+
score_bands_config.score_bands_config.axis_positions = result.score_bands_result.axis_positions
|
|
202
|
+
|
|
203
|
+
# store necessary data to the database. Currently all "voting" related is null bc no voting has happened yet.
|
|
204
|
+
score_bands_info = GDMSCOREBandInformation(
|
|
205
|
+
user_votes={}, user_confirms=[], score_bands_config=score_bands_config, score_bands_result=result
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Add group iteration and related stuff, then set new iteration to head.
|
|
209
|
+
iteration: GroupIteration = GroupIteration(
|
|
210
|
+
group_id=group.id,
|
|
211
|
+
problem_id=group.problem_id,
|
|
212
|
+
info_container=score_bands_info,
|
|
213
|
+
notified={},
|
|
214
|
+
state_id=None,
|
|
215
|
+
parent_id=None,
|
|
216
|
+
parent=None,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
session.add(iteration)
|
|
220
|
+
session.commit()
|
|
221
|
+
session.refresh(iteration)
|
|
222
|
+
|
|
223
|
+
group.head_iteration_id = iteration.id
|
|
224
|
+
session.add(group)
|
|
225
|
+
session.commit()
|
|
226
|
+
session.refresh(group)
|
|
227
|
+
|
|
228
|
+
# Actually, return just the newly created score band data.
|
|
229
|
+
return GDMSCOREBandsHistoryResponse(
|
|
230
|
+
history=[
|
|
231
|
+
GDMSCOREBandsResponse(
|
|
232
|
+
group_id=group.id,
|
|
233
|
+
group_iter_id=group.head_iteration_id,
|
|
234
|
+
latest_iteration=result.iteration,
|
|
235
|
+
result=result.score_bands_result,
|
|
236
|
+
)
|
|
237
|
+
]
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@router.post("/get-votes-and-confirms")
|
|
242
|
+
def get_votes_and_confirms(
|
|
243
|
+
request: GroupInfoRequest,
|
|
244
|
+
user: Annotated[User, Depends(get_current_user)],
|
|
245
|
+
session: Annotated[Session, Depends(get_session)],
|
|
246
|
+
) -> JSONResponse:
|
|
247
|
+
"""Returns the current status of votes and confirmations in current iteration.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
request (GroupInfoRequest): The group we'd like the info on.
|
|
251
|
+
user (Annotated[User, Depends): The user that requests the data.
|
|
252
|
+
session (Annotated[Session, Depends): The database session.
|
|
253
|
+
|
|
254
|
+
Raises:
|
|
255
|
+
HTTPException: If group doesn't exists etc errors.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
JSONResponse: A response containing the votes and confirmations.
|
|
259
|
+
"""
|
|
260
|
+
group: Group = session.exec(select(Group).where(Group.id == request.group_id)).first()
|
|
261
|
+
if not Group:
|
|
262
|
+
raise HTTPException(
|
|
263
|
+
detail=f"Group with ID {request.group_id} not found!", status_code=status.HTTP_404_NOT_FOUND
|
|
264
|
+
)
|
|
265
|
+
if group.head_iteration_id is None:
|
|
266
|
+
raise HTTPException(detail="Group hasn't been initialized!", status_code=status.HTTP_400_BAD_REQUEST)
|
|
267
|
+
user_ids = group.user_ids
|
|
268
|
+
user_ids.append(group.owner_id)
|
|
269
|
+
if user.id not in user_ids:
|
|
270
|
+
raise HTTPException(detail="Unauthorized user!", status_code=status.HTTP_401_UNAUTHORIZED)
|
|
271
|
+
|
|
272
|
+
iteration = session.exec(select(GroupIteration).where(GroupIteration.id == group.head_iteration_id)).first()
|
|
273
|
+
votes = iteration.info_container.user_votes
|
|
274
|
+
confirms = iteration.info_container.user_confirms
|
|
275
|
+
|
|
276
|
+
return JSONResponse(content={"votes": votes, "confirms": confirms})
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@router.post("/revert")
|
|
280
|
+
async def revert(
|
|
281
|
+
request: GDMSCOREBandsRevertRequest,
|
|
282
|
+
user: Annotated[User, Depends(get_current_user)],
|
|
283
|
+
session: Annotated[Session, Depends(get_session)],
|
|
284
|
+
) -> JSONResponse:
|
|
285
|
+
"""Revert to a previous iteration. Usable only by the analyst.
|
|
286
|
+
|
|
287
|
+
This implies that we're gonna need to see ALL previous iterations I'd say.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
request (GDMSCOREBandsRevertRequest): The request containing group id and iteration number.
|
|
291
|
+
user (Annotated[User, Depends): The current user.
|
|
292
|
+
session (Annotated[Session, Depends): The database session.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
JSONResponse: Acknowledgement of the revert.
|
|
296
|
+
"""
|
|
297
|
+
group: Group = session.exec(select(Group).where(Group.id == request.group_id)).first()
|
|
298
|
+
if user.id is not group.owner_id:
|
|
299
|
+
raise HTTPException(
|
|
300
|
+
detail="Reverting can only be done by the group owner!", status_code=status.HTTP_401_UNAUTHORIZED
|
|
301
|
+
)
|
|
302
|
+
if not group:
|
|
303
|
+
raise HTTPException(
|
|
304
|
+
detail=f"Group with ID {request.group_id} not found!", status_code=status.HTTP_404_NOT_FOUND
|
|
305
|
+
)
|
|
306
|
+
user_ids = group.user_ids
|
|
307
|
+
user_ids.append(group.owner_id)
|
|
308
|
+
if user.id not in user_ids:
|
|
309
|
+
raise HTTPException(
|
|
310
|
+
detail=f"User with ID {user.id} is not part of group with ID {group.id}",
|
|
311
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
312
|
+
)
|
|
313
|
+
group_id = request.group_id
|
|
314
|
+
group_mgr: GDMScoreBandsManager = await manager.get_group_manager(
|
|
315
|
+
group_id=group_id, method="gdm-score-bands", db_session=session
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
await group_mgr.revert(user=user, group=group, session=session, group_iteration_number=request.iteration_number)
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.exception("Found an error when trying to revert to a previous iteration.")
|
|
322
|
+
raise HTTPException(
|
|
323
|
+
detail=f"Internal server error: {e}", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
324
|
+
) from e
|
|
325
|
+
|
|
326
|
+
return JSONResponse(content={"message": "Reverted iteration."})
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
@router.post("/configure")
|
|
330
|
+
async def configure_gdm(
|
|
331
|
+
config: SCOREBandsGDMConfig,
|
|
332
|
+
group_id: int,
|
|
333
|
+
user: Annotated[User, Depends(get_current_user)],
|
|
334
|
+
session: Annotated[Session, Depends(get_session)],
|
|
335
|
+
) -> JSONResponse:
|
|
336
|
+
"""Configure the SCORE Bands settings.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
config (SCOREBandsGDMConfig): The configuration object
|
|
340
|
+
group_id (int): group id
|
|
341
|
+
user (Annotated[User, Depends): The user doing the request
|
|
342
|
+
session (Annotated[Session, Depends): The database session.
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
JSONResponse: Acknowledgement that yeah ok reconfigured.
|
|
346
|
+
"""
|
|
347
|
+
group: Group = session.exec(select(Group).where(Group.id == group_id)).first()
|
|
348
|
+
if user.id is not group.owner_id:
|
|
349
|
+
raise HTTPException(
|
|
350
|
+
detail="Reverting can only be done by the group owner!", status_code=status.HTTP_401_UNAUTHORIZED
|
|
351
|
+
)
|
|
352
|
+
if not group:
|
|
353
|
+
raise HTTPException(detail=f"Group with ID {group_id} not found!", status_code=status.HTTP_404_NOT_FOUND)
|
|
354
|
+
user_ids = group.user_ids
|
|
355
|
+
user_ids.append(group.owner_id)
|
|
356
|
+
if user.id not in user_ids:
|
|
357
|
+
raise HTTPException(
|
|
358
|
+
detail=f"User with ID {user.id} is not part of group with ID {group.id}",
|
|
359
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
360
|
+
)
|
|
361
|
+
group_mgr: GDMScoreBandsManager = await manager.get_group_manager(
|
|
362
|
+
group_id=group_id, method="gdm-score-bands", db_session=session
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
await group_mgr.configure(
|
|
367
|
+
config=config,
|
|
368
|
+
group=group,
|
|
369
|
+
session=session,
|
|
370
|
+
)
|
|
371
|
+
except Exception as e:
|
|
372
|
+
logger.exception("Found an error when trying to configure SCORE band settings.")
|
|
373
|
+
raise HTTPException(
|
|
374
|
+
detail=f"Internal server error: {e}", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
375
|
+
) from e
|
|
376
|
+
|
|
377
|
+
return JSONResponse(content={"message": "Configured. Re-clustered."})
|