Skip to content

Commit f0d7ecc

Browse files
committed
new dataclass object submission_bundle
1 parent 9ca2448 commit f0d7ecc

File tree

1 file changed

+313
-0
lines changed

1 file changed

+313
-0
lines changed
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
from dataclasses import dataclass, field
2+
from typing import Dict, List, Optional, Protocol, Union, TYPE_CHECKING
3+
4+
from typing_extensions import Self
5+
6+
from synapseclient import Synapse
7+
from synapseclient.api import submission_services
8+
from synapseclient.core.async_utils import async_to_sync, otel_trace_method
9+
from synapseclient.models.mixins.access_control import AccessControllable
10+
11+
if TYPE_CHECKING:
12+
from synapseclient.models.submission import Submission
13+
from synapseclient.models.submission_status import SubmissionStatus
14+
15+
16+
class SubmissionBundleSynchronousProtocol(Protocol):
17+
"""Protocol defining the synchronous interface for SubmissionBundle operations."""
18+
19+
@staticmethod
20+
def get_evaluation_submission_bundles(
21+
evaluation_id: str,
22+
status: Optional[str] = None,
23+
limit: int = 10,
24+
offset: int = 0,
25+
*,
26+
synapse_client: Optional[Synapse] = None,
27+
) -> List["SubmissionBundle"]:
28+
"""
29+
Gets a collection of bundled Submissions and SubmissionStatuses to a given Evaluation.
30+
31+
Arguments:
32+
evaluation_id: The ID of the specified Evaluation.
33+
status: Optionally filter submission bundles by status.
34+
limit: Limits the number of entities that will be fetched for this page.
35+
When null it will default to 10, max value 100. Default to 10.
36+
offset: The offset index determines where this page will start from.
37+
An index of 0 is the first entity. Default to 0.
38+
synapse_client: If not passed in and caching was not disabled by
39+
`Synapse.allow_client_caching(False)` this will use the last created
40+
instance from the Synapse class constructor.
41+
42+
Returns:
43+
A list of SubmissionBundle objects containing the submission bundles
44+
for the evaluation queue.
45+
46+
Example: Getting submission bundles for an evaluation
47+
```python
48+
from synapseclient import Synapse
49+
from synapseclient.models import SubmissionBundle
50+
51+
syn = Synapse()
52+
syn.login()
53+
54+
bundles = SubmissionBundle.get_evaluation_submission_bundles(
55+
evaluation_id="9614543",
56+
status="SCORED",
57+
limit=50
58+
)
59+
print(f"Found {len(bundles)} submission bundles")
60+
for bundle in bundles:
61+
print(f"Submission ID: {bundle.submission.id if bundle.submission else 'N/A'}")
62+
```
63+
"""
64+
return []
65+
66+
@staticmethod
67+
def get_user_submission_bundles(
68+
evaluation_id: str,
69+
limit: int = 10,
70+
offset: int = 0,
71+
*,
72+
synapse_client: Optional[Synapse] = None,
73+
) -> List["SubmissionBundle"]:
74+
"""
75+
Gets the requesting user's bundled Submissions and SubmissionStatuses to a specified Evaluation.
76+
77+
Arguments:
78+
evaluation_id: The ID of the specified Evaluation.
79+
limit: Limits the number of entities that will be fetched for this page.
80+
When null it will default to 10. Default to 10.
81+
offset: The offset index determines where this page will start from.
82+
An index of 0 is the first entity. Default to 0.
83+
synapse_client: If not passed in and caching was not disabled by
84+
`Synapse.allow_client_caching(False)` this will use the last created
85+
instance from the Synapse class constructor.
86+
87+
Returns:
88+
A list of SubmissionBundle objects containing the requesting user's
89+
submission bundles for the evaluation queue.
90+
91+
Example: Getting user submission bundles
92+
```python
93+
from synapseclient import Synapse
94+
from synapseclient.models import SubmissionBundle
95+
96+
syn = Synapse()
97+
syn.login()
98+
99+
bundles = SubmissionBundle.get_user_submission_bundles(
100+
evaluation_id="9614543",
101+
limit=25
102+
)
103+
print(f"Found {len(bundles)} user submission bundles")
104+
for bundle in bundles:
105+
print(f"Submission ID: {bundle.submission.id if bundle.submission else 'N/A'}")
106+
```
107+
"""
108+
return []
109+
110+
111+
@dataclass
112+
@async_to_sync
113+
class SubmissionBundle(
114+
SubmissionBundleSynchronousProtocol,
115+
AccessControllable,
116+
):
117+
"""A `SubmissionBundle` object represents a bundle containing a Synapse Submission
118+
and its accompanying SubmissionStatus. This bundle provides convenient access to both
119+
the submission data and its current status in a single object.
120+
<https://rest-docs.synapse.org/rest/org/sagebionetworks/evaluation/model/SubmissionBundle.html>
121+
122+
Attributes:
123+
submission: A Submission to a Synapse Evaluation is a pointer to a versioned Entity.
124+
Submissions are immutable, so we archive a copy of the EntityBundle at the time of submission.
125+
submission_status: A SubmissionStatus is a secondary, mutable object associated with a Submission.
126+
This object should be used to contain scoring data about the Submission.
127+
128+
Example: Retrieve submission bundles for an evaluation.
129+
```python
130+
from synapseclient import Synapse
131+
from synapseclient.models import SubmissionBundle
132+
133+
syn = Synapse()
134+
syn.login()
135+
136+
# Get all submission bundles for an evaluation
137+
bundles = SubmissionBundle.get_evaluation_submission_bundles(
138+
evaluation_id="9614543",
139+
status="SCORED"
140+
)
141+
142+
for bundle in bundles:
143+
print(f"Submission ID: {bundle.submission.id if bundle.submission else 'N/A'}")
144+
print(f"Status: {bundle.submission_status.status if bundle.submission_status else 'N/A'}")
145+
```
146+
"""
147+
148+
submission: Optional["Submission"] = None
149+
"""
150+
A Submission to a Synapse Evaluation is a pointer to a versioned Entity.
151+
Submissions are immutable, so we archive a copy of the EntityBundle at the time of submission.
152+
"""
153+
154+
submission_status: Optional["SubmissionStatus"] = None
155+
"""
156+
A SubmissionStatus is a secondary, mutable object associated with a Submission.
157+
This object should be used to contain scoring data about the Submission.
158+
"""
159+
160+
_last_persistent_instance: Optional["SubmissionBundle"] = field(
161+
default=None, repr=False, compare=False
162+
)
163+
"""The last persistent instance of this object. This is used to determine if the
164+
object has been changed and needs to be updated in Synapse."""
165+
166+
def fill_from_dict(
167+
self, synapse_submission_bundle: Dict[str, Union[bool, str, int, Dict]]
168+
) -> "SubmissionBundle":
169+
"""
170+
Converts a response from the REST API into this dataclass.
171+
172+
Arguments:
173+
synapse_submission_bundle: The response from the REST API.
174+
175+
Returns:
176+
The SubmissionBundle object.
177+
"""
178+
from synapseclient.models.submission import Submission
179+
from synapseclient.models.submission_status import SubmissionStatus
180+
181+
submission_dict = synapse_submission_bundle.get("submission", None)
182+
if submission_dict:
183+
self.submission = Submission().fill_from_dict(submission_dict)
184+
else:
185+
self.submission = None
186+
187+
submission_status_dict = synapse_submission_bundle.get("submissionStatus", None)
188+
if submission_status_dict:
189+
self.submission_status = SubmissionStatus().fill_from_dict(submission_status_dict)
190+
else:
191+
self.submission_status = None
192+
193+
return self
194+
195+
@staticmethod
196+
async def get_evaluation_submission_bundles_async(
197+
evaluation_id: str,
198+
status: Optional[str] = None,
199+
limit: int = 10,
200+
offset: int = 0,
201+
*,
202+
synapse_client: Optional[Synapse] = None,
203+
) -> List["SubmissionBundle"]:
204+
"""
205+
Gets a collection of bundled Submissions and SubmissionStatuses to a given Evaluation.
206+
207+
Arguments:
208+
evaluation_id: The ID of the specified Evaluation.
209+
status: Optionally filter submission bundles by status.
210+
limit: Limits the number of entities that will be fetched for this page.
211+
When null it will default to 10, max value 100. Default to 10.
212+
offset: The offset index determines where this page will start from.
213+
An index of 0 is the first entity. Default to 0.
214+
synapse_client: If not passed in and caching was not disabled by
215+
`Synapse.allow_client_caching(False)` this will use the last created
216+
instance from the Synapse class constructor.
217+
218+
Returns:
219+
A list of SubmissionBundle objects containing the submission bundles
220+
for the evaluation queue.
221+
222+
Note:
223+
The caller must be granted the ACCESS_TYPE.READ_PRIVATE_SUBMISSION on the specified Evaluation.
224+
225+
Example: Getting submission bundles for an evaluation
226+
```python
227+
from synapseclient import Synapse
228+
from synapseclient.models import SubmissionBundle
229+
230+
syn = Synapse()
231+
syn.login()
232+
233+
bundles = await SubmissionBundle.get_evaluation_submission_bundles_async(
234+
evaluation_id="9614543",
235+
status="SCORED",
236+
limit=50
237+
)
238+
print(f"Found {len(bundles)} submission bundles")
239+
for bundle in bundles:
240+
print(f"Submission ID: {bundle.submission.id if bundle.submission else 'N/A'}")
241+
```
242+
"""
243+
response = await submission_services.get_evaluation_submission_bundles(
244+
evaluation_id=evaluation_id,
245+
status=status,
246+
limit=limit,
247+
offset=offset,
248+
synapse_client=synapse_client,
249+
)
250+
251+
bundles = []
252+
for bundle_dict in response.get("results", []):
253+
bundle = SubmissionBundle().fill_from_dict(bundle_dict)
254+
bundles.append(bundle)
255+
256+
return bundles
257+
258+
@staticmethod
259+
async def get_user_submission_bundles_async(
260+
evaluation_id: str,
261+
limit: int = 10,
262+
offset: int = 0,
263+
*,
264+
synapse_client: Optional[Synapse] = None,
265+
) -> List["SubmissionBundle"]:
266+
"""
267+
Gets the requesting user's bundled Submissions and SubmissionStatuses to a specified Evaluation.
268+
269+
Arguments:
270+
evaluation_id: The ID of the specified Evaluation.
271+
limit: Limits the number of entities that will be fetched for this page.
272+
When null it will default to 10. Default to 10.
273+
offset: The offset index determines where this page will start from.
274+
An index of 0 is the first entity. Default to 0.
275+
synapse_client: If not passed in and caching was not disabled by
276+
`Synapse.allow_client_caching(False)` this will use the last created
277+
instance from the Synapse class constructor.
278+
279+
Returns:
280+
A list of SubmissionBundle objects containing the requesting user's
281+
submission bundles for the evaluation queue.
282+
283+
Example: Getting user submission bundles
284+
```python
285+
from synapseclient import Synapse
286+
from synapseclient.models import SubmissionBundle
287+
288+
syn = Synapse()
289+
syn.login()
290+
291+
bundles = await SubmissionBundle.get_user_submission_bundles_async(
292+
evaluation_id="9614543",
293+
limit=25
294+
)
295+
print(f"Found {len(bundles)} user submission bundles")
296+
for bundle in bundles:
297+
print(f"Submission ID: {bundle.submission.id if bundle.submission else 'N/A'}")
298+
```
299+
"""
300+
response = await submission_services.get_user_submission_bundles(
301+
evaluation_id=evaluation_id,
302+
limit=limit,
303+
offset=offset,
304+
synapse_client=synapse_client,
305+
)
306+
307+
# Convert response to list of SubmissionBundle objects
308+
bundles = []
309+
for bundle_dict in response.get("results", []):
310+
bundle = SubmissionBundle().fill_from_dict(bundle_dict)
311+
bundles.append(bundle)
312+
313+
return bundles

0 commit comments

Comments
 (0)