|
5 | 5 | data or metadata in Synapse. |
6 | 6 | """ |
7 | 7 |
|
| 8 | +import os |
8 | 9 | from dataclasses import dataclass, field, replace |
9 | 10 | from typing import Any, AsyncGenerator, Dict, Generator, Optional, Protocol, Union |
10 | 11 |
|
|
28 | 29 | from synapseclient.core.constants.concrete_types import ( |
29 | 30 | CREATE_GRID_REQUEST, |
30 | 31 | FILE_BASED_METADATA_TASK_PROPERTIES, |
| 32 | + GRID_CSV_IMPORT_REQUEST, |
31 | 33 | GRID_RECORD_SET_EXPORT_REQUEST, |
32 | 34 | LIST_GRID_SESSIONS_REQUEST, |
33 | 35 | LIST_GRID_SESSIONS_RESPONSE, |
@@ -1078,6 +1080,64 @@ def to_synapse_request(self) -> Dict[str, Any]: |
1078 | 1080 | return request_dict |
1079 | 1081 |
|
1080 | 1082 |
|
| 1083 | +@dataclass |
| 1084 | +class GridCsvImportRequest(AsynchronousCommunicator): |
| 1085 | + """ |
| 1086 | + Request to import a CSV file into an existing grid session. |
| 1087 | +
|
| 1088 | + Represents a Synapse GridCsvImportRequest: |
| 1089 | + POST /grid/import/csv/async/start |
| 1090 | + GET /grid/import/csv/async/get/{asyncToken} |
| 1091 | +
|
| 1092 | + Attributes: |
| 1093 | + concrete_type: The concrete type for the request |
| 1094 | + session_id: The grid session ID to import the CSV into |
| 1095 | + file_handle_id: The file handle ID of the CSV file to import |
| 1096 | + response_session_id: The session ID from the import response (populated from response) |
| 1097 | + """ |
| 1098 | + |
| 1099 | + concrete_type: str = GRID_CSV_IMPORT_REQUEST |
| 1100 | + """The concrete type for the request""" |
| 1101 | + |
| 1102 | + session_id: Optional[str] = None |
| 1103 | + """The grid session ID to import the CSV into""" |
| 1104 | + |
| 1105 | + file_handle_id: Optional[str] = None |
| 1106 | + """The file handle ID of the CSV file to import""" |
| 1107 | + |
| 1108 | + response_session_id: Optional[str] = None |
| 1109 | + """The session ID from the import response (populated from response)""" |
| 1110 | + |
| 1111 | + def fill_from_dict( |
| 1112 | + self, synapse_response: Dict[str, Any] |
| 1113 | + ) -> "GridCsvImportRequest": |
| 1114 | + """ |
| 1115 | + Converts a response from the REST API into this dataclass. |
| 1116 | +
|
| 1117 | + Arguments: |
| 1118 | + synapse_response: The response from the REST API. |
| 1119 | +
|
| 1120 | + Returns: |
| 1121 | + The GridCsvImportRequest object. |
| 1122 | + """ |
| 1123 | + self.response_session_id = synapse_response.get("sessionId", None) |
| 1124 | + return self |
| 1125 | + |
| 1126 | + def to_synapse_request(self) -> Dict[str, Any]: |
| 1127 | + """ |
| 1128 | + Converts this dataclass to a dictionary suitable for a Synapse REST API request. |
| 1129 | +
|
| 1130 | + Returns: |
| 1131 | + A dictionary representation of this object for API requests. |
| 1132 | + """ |
| 1133 | + request_dict: Dict[str, Any] = {"concreteType": self.concrete_type} |
| 1134 | + if self.session_id is not None: |
| 1135 | + request_dict["sessionId"] = self.session_id |
| 1136 | + if self.file_handle_id is not None: |
| 1137 | + request_dict["fileHandleId"] = self.file_handle_id |
| 1138 | + return request_dict |
| 1139 | + |
| 1140 | + |
1081 | 1141 | @dataclass |
1082 | 1142 | class GridSession: |
1083 | 1143 | """ |
@@ -1373,6 +1433,69 @@ def delete(self, *, synapse_client: Optional[Synapse] = None) -> None: |
1373 | 1433 | """ |
1374 | 1434 | return None |
1375 | 1435 |
|
| 1436 | + def import_csv( |
| 1437 | + self, |
| 1438 | + file_handle_id: Optional[str] = None, |
| 1439 | + path: Optional[str] = None, |
| 1440 | + *, |
| 1441 | + timeout: int = 120, |
| 1442 | + synapse_client: Optional[Synapse] = None, |
| 1443 | + ) -> "Grid": |
| 1444 | + """ |
| 1445 | + Import a CSV file into the grid session. |
| 1446 | +
|
| 1447 | + Accepts either a pre-uploaded `file_handle_id` or a local `path` that will |
| 1448 | + be uploaded automatically before importing. |
| 1449 | +
|
| 1450 | + Arguments: |
| 1451 | + file_handle_id: The Synapse file handle ID of the CSV to import. |
| 1452 | + Mutually exclusive with `path`. |
| 1453 | + path: Local path to a CSV file. If provided, the file will be uploaded |
| 1454 | + via multipart upload and the resulting file handle ID used for import. |
| 1455 | + Mutually exclusive with `file_handle_id`. |
| 1456 | + timeout: The number of seconds to wait for the async job to complete. |
| 1457 | + Defaults to 120. |
| 1458 | + synapse_client: If not passed in and caching was not disabled by |
| 1459 | + `Synapse.allow_client_caching(False)` this will use the last created |
| 1460 | + instance from the Synapse class constructor. |
| 1461 | +
|
| 1462 | + Returns: |
| 1463 | + The Grid object (self). |
| 1464 | +
|
| 1465 | + Raises: |
| 1466 | + ValueError: If `session_id` is not set. |
| 1467 | + ValueError: If neither `file_handle_id` nor `path` is provided. |
| 1468 | +
|
| 1469 | + Example: Import CSV by file handle ID |
| 1470 | + |
| 1471 | +
|
| 1472 | + ```python |
| 1473 | + from synapseclient import Synapse |
| 1474 | + from synapseclient.models import Grid |
| 1475 | +
|
| 1476 | + syn = Synapse() |
| 1477 | + syn.login() |
| 1478 | +
|
| 1479 | + grid = Grid(session_id="abc-123-def") |
| 1480 | + grid = grid.import_csv(file_handle_id="12345678") |
| 1481 | + ``` |
| 1482 | +
|
| 1483 | + Example: Import CSV from a local path |
| 1484 | + |
| 1485 | +
|
| 1486 | + ```python |
| 1487 | + from synapseclient import Synapse |
| 1488 | + from synapseclient.models import Grid |
| 1489 | +
|
| 1490 | + syn = Synapse() |
| 1491 | + syn.login() |
| 1492 | +
|
| 1493 | + grid = Grid(session_id="abc-123-def") |
| 1494 | + grid = grid.import_csv(path="/path/to/data.csv") |
| 1495 | + ``` |
| 1496 | + """ |
| 1497 | + return self |
| 1498 | + |
1376 | 1499 | @classmethod |
1377 | 1500 | def list( |
1378 | 1501 | cls, |
@@ -1838,3 +1961,106 @@ async def main(): |
1838 | 1961 | await delete_grid_session( |
1839 | 1962 | session_id=self.session_id, synapse_client=synapse_client |
1840 | 1963 | ) |
| 1964 | + |
| 1965 | + async def import_csv_async( |
| 1966 | + self, |
| 1967 | + file_handle_id: Optional[str] = None, |
| 1968 | + path: Optional[str] = None, |
| 1969 | + *, |
| 1970 | + timeout: int = 120, |
| 1971 | + synapse_client: Optional[Synapse] = None, |
| 1972 | + ) -> "Grid": |
| 1973 | + """ |
| 1974 | + Import a CSV file into the grid session. |
| 1975 | +
|
| 1976 | + Accepts either a pre-uploaded `file_handle_id` or a local `path` that will |
| 1977 | + be uploaded automatically before importing. |
| 1978 | +
|
| 1979 | + Arguments: |
| 1980 | + file_handle_id: The Synapse file handle ID of the CSV to import. |
| 1981 | + Mutually exclusive with `path`. |
| 1982 | + path: Local path to a CSV file. If provided, the file will be uploaded |
| 1983 | + via multipart upload and the resulting file handle ID used for import. |
| 1984 | + Mutually exclusive with `file_handle_id`. |
| 1985 | + timeout: The number of seconds to wait for the async job to complete. |
| 1986 | + Defaults to 120. |
| 1987 | + synapse_client: If not passed in and caching was not disabled by |
| 1988 | + `Synapse.allow_client_caching(False)` this will use the last created |
| 1989 | + instance from the Synapse class constructor. |
| 1990 | +
|
| 1991 | + Returns: |
| 1992 | + The Grid object (self). |
| 1993 | +
|
| 1994 | + Raises: |
| 1995 | + ValueError: If `session_id` is not set. |
| 1996 | + ValueError: If neither `file_handle_id` nor `path` is provided. |
| 1997 | +
|
| 1998 | + Example: Import CSV by file handle ID asynchronously |
| 1999 | + |
| 2000 | +
|
| 2001 | + ```python |
| 2002 | + import asyncio |
| 2003 | + from synapseclient import Synapse |
| 2004 | + from synapseclient.models import Grid |
| 2005 | +
|
| 2006 | + syn = Synapse() |
| 2007 | + syn.login() |
| 2008 | +
|
| 2009 | + async def main(): |
| 2010 | + grid = Grid(session_id="abc-123-def") |
| 2011 | + grid = await grid.import_csv_async(file_handle_id="12345678") |
| 2012 | +
|
| 2013 | + asyncio.run(main()) |
| 2014 | + ``` |
| 2015 | +
|
| 2016 | + Example: Import CSV from a local path asynchronously |
| 2017 | + |
| 2018 | +
|
| 2019 | + ```python |
| 2020 | + import asyncio |
| 2021 | + from synapseclient import Synapse |
| 2022 | + from synapseclient.models import Grid |
| 2023 | +
|
| 2024 | + syn = Synapse() |
| 2025 | + syn.login() |
| 2026 | +
|
| 2027 | + async def main(): |
| 2028 | + grid = Grid(session_id="abc-123-def") |
| 2029 | + grid = await grid.import_csv_async(path="/path/to/data.csv") |
| 2030 | +
|
| 2031 | + asyncio.run(main()) |
| 2032 | + ``` |
| 2033 | + """ |
| 2034 | + if not self.session_id: |
| 2035 | + raise ValueError("session_id is required to import CSV into a GridSession") |
| 2036 | + if not file_handle_id and not path: |
| 2037 | + raise ValueError( |
| 2038 | + "Either file_handle_id or path must be provided to import a CSV" |
| 2039 | + ) |
| 2040 | + |
| 2041 | + trace.get_current_span().set_attributes( |
| 2042 | + { |
| 2043 | + "synapse.session_id": self.session_id or "", |
| 2044 | + } |
| 2045 | + ) |
| 2046 | + |
| 2047 | + client = Synapse.get_client(synapse_client=synapse_client) |
| 2048 | + |
| 2049 | + if path: |
| 2050 | + from synapseclient.core.upload.multipart_upload_async import ( |
| 2051 | + multipart_upload_file_async, |
| 2052 | + ) |
| 2053 | + |
| 2054 | + path = os.path.expanduser(path) |
| 2055 | + file_handle_id = await multipart_upload_file_async( |
| 2056 | + syn=client, file_path=path, content_type="text/csv" |
| 2057 | + ) |
| 2058 | + |
| 2059 | + import_request = GridCsvImportRequest( |
| 2060 | + session_id=self.session_id, file_handle_id=file_handle_id |
| 2061 | + ) |
| 2062 | + await import_request.send_job_and_wait_async( |
| 2063 | + timeout=timeout, synapse_client=client |
| 2064 | + ) |
| 2065 | + |
| 2066 | + return self |
0 commit comments