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