Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
98bfc00
implement oop to create a form group
Dec 4, 2025
9190e95
added data class; be able to create form data using form group id
Dec 4, 2025
73bbb5d
allow list form reviewer
Dec 4, 2025
704112e
make validating state enum separate from individual function; add lis…
Dec 4, 2025
bcbb626
create a helper function for download
Dec 4, 2025
56b2b49
add example; fix type hint
Dec 5, 2025
d33a026
use protocol; use wrapper
Dec 5, 2025
a98ca19
adjust space; add documentation
Dec 5, 2025
e846755
export more functions; adjust import
Dec 5, 2025
7fa49eb
make imports better; add import in the examples; add test
Dec 5, 2025
61b4d84
add mixin test
Dec 5, 2025
82e0033
update import
Dec 5, 2025
c4e05a7
combining list data and list reviewer
Dec 5, 2025
e3119dd
raise value error if filter by state is empty
Dec 5, 2025
ba58ee1
rename to form submission status
Dec 5, 2025
148f178
edit docstring; just use one call
Dec 5, 2025
2f38b6b
remove comments in the docstring
Dec 5, 2025
2a62c48
add test; simplify code
Dec 8, 2025
5d9daba
move test to async dir; also add test for download; remove syn fixtur…
Dec 8, 2025
b189f11
remove async in the function name in api
Dec 8, 2025
77365c9
add sync version of the test
Dec 8, 2025
9eb3af1
add integration test async
Dec 8, 2025
c2be473
add test for sync
Dec 8, 2025
a6c09d8
rename test
Dec 8, 2025
2cd5999
Merge branch 'develop' into synpy-1674
Dec 9, 2025
cab8a3f
update comment in the code
Dec 9, 2025
c90891b
fix docstring and also docstring example
Dec 9, 2025
913e910
fix test name
Dec 9, 2025
39d0a5f
remove unused import
Dec 9, 2025
725c415
add dataclass docstring; turn filter_by_state to be required; edit test
Dec 10, 2025
0e85642
edit error message; edit test
Dec 10, 2025
4ba4616
fix test
Dec 10, 2025
158e2ba
remove allow_waiting_submission parameter
Dec 10, 2025
eed6712
change filter by state to required
Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/reference/experimental/async/form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# FormGroup and Form

Contained within this file are experimental interfaces for working with the Synapse Python
Client. Unless otherwise noted these interfaces are subject to change at any time. Use
at your own risk.

## API Reference

::: synapseclient.models.FormGroup
options:
inherited_members: true
members:
- create_async


::: synapseclient.models.FormData
options:
inherited_members: true
members:
- create_async
- list_async
- download_async
1 change: 1 addition & 0 deletions docs/reference/experimental/mixins/form_data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: synapseclient.models.mixins.FormData
1 change: 1 addition & 0 deletions docs/reference/experimental/mixins/form_group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: synapseclient.models.mixins.FormGroup
22 changes: 22 additions & 0 deletions docs/reference/experimental/sync/form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# FormGroup and Form

Contained within this file are experimental interfaces for working with the Synapse Python
Client. Unless otherwise noted these interfaces are subject to change at any time. Use
at your own risk.

## API Reference

::: synapseclient.models.FormGroup
options:
inherited_members: true
members:
- create


::: synapseclient.models.FormData
options:
inherited_members: true
members:
- create
- list
- download
4 changes: 4 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ nav:
- Functional Interfaces: reference/experimental/functional_interfaces.md
- SchemaOrganization: reference/experimental/sync/schema_organization.md
- JSONSchema: reference/experimental/sync/json_schema.md
- FormGroup and Form: reference/experimental/sync/form.md
- Extensions:
- Curator: reference/extensions/curator.md
- Asynchronous:
Expand All @@ -128,13 +129,16 @@ nav:
- Link: reference/experimental/async/link_entity.md
- SchemaOrganization: reference/experimental/async/schema_organization.md
- JSONSchema: reference/experimental/async/json_schema.md
- FormGroup and Form: reference/experimental/async/form.md
- Mixins:
- AccessControllable: reference/experimental/mixins/access_controllable.md
- StorableContainer: reference/experimental/mixins/storable_container.md
- AsynchronousCommunicator: reference/experimental/mixins/asynchronous_communicator.md
- FailureStrategy: reference/experimental/mixins/failure_strategy.md
- BaseJSONSchema: reference/experimental/mixins/base_json_schema.md
- ContainerEntityJSONSchema: reference/experimental/mixins/container_json_schema.md
- FormData: reference/experimental/mixins/form_data.md
- FormGroup: reference/experimental/mixins/form_group.md

- Further Reading:
- Home: explanations/home.md
Expand Down
11 changes: 11 additions & 0 deletions synapseclient/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@
put_file_multipart_add,
put_file_multipart_complete,
)
from .form_services import (
create_form_data,
create_form_group,
list_form_data,
list_form_data_sync,
)
from .json_schema_services import (
bind_json_schema_to_entity,
create_organization,
Expand Down Expand Up @@ -282,4 +288,9 @@
"get_evaluation_acl",
"update_evaluation_acl",
"get_evaluation_permissions",
# form services
"create_form_group",
"create_form_data",
"list_form_data",
"list_form_data_sync",
]
171 changes: 171 additions & 0 deletions synapseclient/api/form_services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import json
from typing import TYPE_CHECKING, Any, AsyncGenerator, Generator

from synapseclient.api.api_client import rest_post_paginated_async
from synapseclient.core.async_utils import wrap_async_generator_to_sync_generator

if TYPE_CHECKING:
from synapseclient import Synapse
from synapseclient.models.mixins.form import StateEnum


async def create_form_group(
synapse_client: "Synapse",
name: str,
) -> dict[str, Any]:
"""
<https://rest-docs.synapse.org/rest/POST/form/group.html>
Create a form group asynchronously.

Arguments:
synapse_client: The Synapse client to use for the request.
name: A globally unique name for the group. Required. Between 3 and 256 characters.

Returns:
A Form group object as a dictionary.
Object matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/form/FormGroup.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

return await client.rest_post_async(uri=f"/form/group?name={name}", body={})


async def create_form_data(
synapse_client: "Synapse",
group_id: str,
form_change_request: dict[str, Any],
) -> dict[str, Any]:
"""
<https://rest-docs.synapse.org/rest/POST/form/data.html>
Create a new FormData object. The caller will own the resulting object and will have access to read, update, and delete the FormData object.

Arguments:
synapse_client: The Synapse client to use for the request.
group_id: The ID of the form group.
form_change_request: a dictionary of form change request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/form/FormChangeRequest.html>.

Returns:
A Form data object as a dictionary.
Object matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/form/FormData.html>

Note: The caller must have the SUBMIT permission on the FormGroup to create/update/submit FormData.
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

return await client.rest_post_async(
uri=f"/form/data?groupId={group_id}",
body=json.dumps(form_change_request),
)


async def list_form_data(
synapse_client: "Synapse",
group_id: str,
filter_by_state: list["StateEnum"],
as_reviewer: bool = False,
) -> AsyncGenerator[dict[str, Any], None]:
"""
List FormData objects and their associated status that match the filters of the provided request.

When as_reviewer=False: <https://rest-docs.synapse.org/rest/POST/form/data/list.html>
Returns FormData objects owned by the caller. Only objects owned by the caller will be returned.

When as_reviewer=True: <https://rest-docs.synapse.org/rest/POST/form/data/list/reviewer.html>
Returns FormData objects for the entire group. This is used by service accounts to review submissions.
Requires READ_PRIVATE_SUBMISSION permission on the FormGroup.

Arguments:
synapse_client: The Synapse client to use for the request.
group_id: The ID of the form group. Required.
filter_by_state: list of StateEnum values to filter the FormData objects.
When as_reviewer=False (default), valid values are:
- StateEnum.WAITING_FOR_SUBMISSION
- StateEnum.SUBMITTED_WAITING_FOR_REVIEW
- StateEnum.ACCEPTED
- StateEnum.REJECTED

When as_reviewer=True, valid values are:
- StateEnum.SUBMITTED_WAITING_FOR_REVIEW (default if None)
- StateEnum.ACCEPTED
- StateEnum.REJECTED
Note: WAITING_FOR_SUBMISSION is NOT allowed when as_reviewer=True.

as_reviewer: If True, uses the reviewer endpoint to list FormData for the entire group.
If False (default), lists only FormData owned by the caller.

Yields:
A single page of FormData objects matching the request.
Object matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/form/ListResponse.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

body: dict[str, Any] = {"groupId": group_id, "filterByState": filter_by_state}

if as_reviewer:
uri = "/form/data/list/reviewer"
else:
uri = "/form/data/list"

async for item in rest_post_paginated_async(
uri=uri,
body=body,
synapse_client=client,
):
yield item


def list_form_data_sync(
synapse_client: "Synapse",
group_id: str,
filter_by_state: list["StateEnum"],
as_reviewer: bool = False,
) -> Generator[dict[str, Any], None, None]:
"""
List FormData objects and their associated status that match the filters of the provided request.

This is the synchronous version of list_form_data_async.

When as_reviewer=False: <https://rest-docs.synapse.org/rest/POST/form/data/list.html>
Returns FormData objects owned by the caller. Only objects owned by the caller will be returned.

When as_reviewer=True: <https://rest-docs.synapse.org/rest/POST/form/data/list/reviewer.html>
Returns FormData objects for the entire group. This is used by service accounts to review submissions.
Requires READ_PRIVATE_SUBMISSION permission on the FormGroup.

Arguments:
synapse_client: The Synapse client to use for the request.
group_id: The ID of the form group. Required.
filter_by_state: list of StateEnum values to filter the FormData objects.
When as_reviewer=False (default), valid values are:
- StateEnum.WAITING_FOR_SUBMISSION
- StateEnum.SUBMITTED_WAITING_FOR_REVIEW
- StateEnum.ACCEPTED
- StateEnum.REJECTED

When as_reviewer=True, valid values are:
- StateEnum.SUBMITTED_WAITING_FOR_REVIEW (default if None)
- StateEnum.ACCEPTED
- StateEnum.REJECTED
Note: WAITING_FOR_SUBMISSION is NOT allowed when as_reviewer=True.

as_reviewer: If True, uses the reviewer endpoint to list FormData for the entire group.
If False (default), lists only FormData owned by the caller.

Yields:
A single page of FormData objects matching the request.
Object matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/form/ListResponse.html>
"""
return wrap_async_generator_to_sync_generator(
list_form_data(
synapse_client=synapse_client,
group_id=group_id,
filter_by_state=filter_by_state,
as_reviewer=as_reviewer,
)
)
4 changes: 4 additions & 0 deletions synapseclient/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from synapseclient.models.evaluation import Evaluation
from synapseclient.models.file import File, FileHandle
from synapseclient.models.folder import Folder
from synapseclient.models.form import FormData, FormGroup
from synapseclient.models.link import Link
from synapseclient.models.materializedview import MaterializedView
from synapseclient.models.mixins.table_components import QueryMixin
Expand Down Expand Up @@ -132,6 +133,9 @@
# JSON Schema models
"SchemaOrganization",
"JSONSchema",
# Form models
"FormGroup",
"FormData",
]

# Static methods to expose as functions
Expand Down
Loading
Loading