gena-datasets 0.1.0__tar.gz
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.
- gena_datasets-0.1.0/LICENSE +21 -0
- gena_datasets-0.1.0/PKG-INFO +67 -0
- gena_datasets-0.1.0/README.md +56 -0
- gena_datasets-0.1.0/pyproject.toml +19 -0
- gena_datasets-0.1.0/setup.cfg +4 -0
- gena_datasets-0.1.0/src/gena_datasets/__init__.py +115 -0
- gena_datasets-0.1.0/src/gena_datasets/py.typed +1 -0
- gena_datasets-0.1.0/src/gena_datasets.egg-info/PKG-INFO +67 -0
- gena_datasets-0.1.0/src/gena_datasets.egg-info/SOURCES.txt +10 -0
- gena_datasets-0.1.0/src/gena_datasets.egg-info/dependency_links.txt +1 -0
- gena_datasets-0.1.0/src/gena_datasets.egg-info/top_level.txt +1 -0
- gena_datasets-0.1.0/tests/test_client.py +61 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TuluAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gena-datasets
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Browse public Gena datasets and submit datasets for review.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: gena,datasets,tuluai,machine-learning
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# `gena-datasets`
|
|
13
|
+
|
|
14
|
+
Python client for browsing public datasets and submitting a dataset to Gena for review.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
python -m pip install gena-datasets
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Read public datasets
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from gena_datasets import GenaDatasetsClient
|
|
26
|
+
|
|
27
|
+
gena = GenaDatasetsClient()
|
|
28
|
+
for item in gena.list_datasets():
|
|
29
|
+
print(item["id"], item["title"], item["license"])
|
|
30
|
+
|
|
31
|
+
dataset = gena.get_dataset("tulueng100-reference")
|
|
32
|
+
print(dataset["content"])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Public catalog access does not require sign-in. `download_dataset(id)` returns the original dataset file contents as text.
|
|
36
|
+
|
|
37
|
+
## Submit a dataset
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
id_token = current_firebase_user.get_id_token()
|
|
41
|
+
submission = gena.submit_dataset(
|
|
42
|
+
{
|
|
43
|
+
"title": "Example collection",
|
|
44
|
+
"description": "A short description of the data and how it was collected.",
|
|
45
|
+
"language": "English",
|
|
46
|
+
"license": "CC BY 4.0",
|
|
47
|
+
"fileName": "examples.jsonl",
|
|
48
|
+
"content": '{"text":"Example"}\n',
|
|
49
|
+
"rightsConfirmed": True,
|
|
50
|
+
},
|
|
51
|
+
id_token=id_token,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
print(submission["datasetId"], submission["status"])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Submissions are private while pending review. A designated Gena moderator must approve a submission before it becomes public. The contributor must have permission to share the data and choose a license that allows publication. Current uploads are limited to 900,000 bytes and `.csv`, `.json`, `.jsonl`, or `.txt` files.
|
|
58
|
+
|
|
59
|
+
## API endpoints
|
|
60
|
+
|
|
61
|
+
- `GET /api/gena/datasets`
|
|
62
|
+
- `GET /api/gena/datasets/{id}`
|
|
63
|
+
- `POST /api/gena/datasets` with a signed-in account token
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# `gena-datasets`
|
|
2
|
+
|
|
3
|
+
Python client for browsing public datasets and submitting a dataset to Gena for review.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
python -m pip install gena-datasets
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Read public datasets
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from gena_datasets import GenaDatasetsClient
|
|
15
|
+
|
|
16
|
+
gena = GenaDatasetsClient()
|
|
17
|
+
for item in gena.list_datasets():
|
|
18
|
+
print(item["id"], item["title"], item["license"])
|
|
19
|
+
|
|
20
|
+
dataset = gena.get_dataset("tulueng100-reference")
|
|
21
|
+
print(dataset["content"])
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Public catalog access does not require sign-in. `download_dataset(id)` returns the original dataset file contents as text.
|
|
25
|
+
|
|
26
|
+
## Submit a dataset
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
id_token = current_firebase_user.get_id_token()
|
|
30
|
+
submission = gena.submit_dataset(
|
|
31
|
+
{
|
|
32
|
+
"title": "Example collection",
|
|
33
|
+
"description": "A short description of the data and how it was collected.",
|
|
34
|
+
"language": "English",
|
|
35
|
+
"license": "CC BY 4.0",
|
|
36
|
+
"fileName": "examples.jsonl",
|
|
37
|
+
"content": '{"text":"Example"}\n',
|
|
38
|
+
"rightsConfirmed": True,
|
|
39
|
+
},
|
|
40
|
+
id_token=id_token,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
print(submission["datasetId"], submission["status"])
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Submissions are private while pending review. A designated Gena moderator must approve a submission before it becomes public. The contributor must have permission to share the data and choose a license that allows publication. Current uploads are limited to 900,000 bytes and `.csv`, `.json`, `.jsonl`, or `.txt` files.
|
|
47
|
+
|
|
48
|
+
## API endpoints
|
|
49
|
+
|
|
50
|
+
- `GET /api/gena/datasets`
|
|
51
|
+
- `GET /api/gena/datasets/{id}`
|
|
52
|
+
- `POST /api/gena/datasets` with a signed-in account token
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gena-datasets"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Browse public Gena datasets and submit datasets for review."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
keywords = ["gena", "datasets", "tuluai", "machine-learning"]
|
|
12
|
+
license = "MIT"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
|
|
15
|
+
[tool.setuptools.packages.find]
|
|
16
|
+
where = ["src"]
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.package-data]
|
|
19
|
+
gena_datasets = ["py.typed"]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Python client for public Gena datasets and moderated submissions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Mapping, Optional
|
|
7
|
+
from urllib.error import HTTPError, URLError
|
|
8
|
+
from urllib.request import Request, urlopen
|
|
9
|
+
|
|
10
|
+
__all__ = ["GenaDatasetsClient", "GenaDatasetsError"]
|
|
11
|
+
|
|
12
|
+
DEFAULT_BASE_URL = "https://tuluai.app"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GenaDatasetsError(RuntimeError):
|
|
16
|
+
"""An API or network error returned by Gena."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, message: str, status: int = 0) -> None:
|
|
19
|
+
super().__init__(message)
|
|
20
|
+
self.status = status
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GenaDatasetsClient:
|
|
24
|
+
"""Access Gena's public catalog and submit datasets for review."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, base_url: str = DEFAULT_BASE_URL, timeout: float = 20.0) -> None:
|
|
27
|
+
self.base_url = base_url.rstrip("/")
|
|
28
|
+
self.timeout = timeout
|
|
29
|
+
|
|
30
|
+
def _request(
|
|
31
|
+
self,
|
|
32
|
+
path: str,
|
|
33
|
+
*,
|
|
34
|
+
method: str = "GET",
|
|
35
|
+
body: Optional[Mapping[str, Any]] = None,
|
|
36
|
+
id_token: Optional[str] = None,
|
|
37
|
+
) -> dict[str, Any]:
|
|
38
|
+
headers = {"Accept": "application/json"}
|
|
39
|
+
data = None
|
|
40
|
+
if body is not None:
|
|
41
|
+
headers["Content-Type"] = "application/json"
|
|
42
|
+
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
|
|
43
|
+
if id_token:
|
|
44
|
+
headers["Authorization"] = "Bearer " + id_token
|
|
45
|
+
|
|
46
|
+
request = Request(
|
|
47
|
+
self.base_url + path,
|
|
48
|
+
data=data,
|
|
49
|
+
headers=headers,
|
|
50
|
+
method=method,
|
|
51
|
+
)
|
|
52
|
+
try:
|
|
53
|
+
with urlopen(request, timeout=self.timeout) as response:
|
|
54
|
+
result = json.loads(response.read().decode("utf-8"))
|
|
55
|
+
except HTTPError as error:
|
|
56
|
+
try:
|
|
57
|
+
payload = json.loads(error.read().decode("utf-8"))
|
|
58
|
+
message = payload.get("error", "The Gena dataset request failed.")
|
|
59
|
+
except (ValueError, AttributeError):
|
|
60
|
+
message = "The Gena dataset request failed."
|
|
61
|
+
raise GenaDatasetsError(str(message), error.code) from None
|
|
62
|
+
except (URLError, TimeoutError, OSError):
|
|
63
|
+
raise GenaDatasetsError("Could not reach the Gena dataset API.") from None
|
|
64
|
+
except (ValueError, UnicodeDecodeError):
|
|
65
|
+
raise GenaDatasetsError("Gena returned an unreadable response.") from None
|
|
66
|
+
|
|
67
|
+
if not isinstance(result, dict):
|
|
68
|
+
raise GenaDatasetsError("Gena returned an unexpected response.")
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
def list_datasets(self) -> list[dict[str, Any]]:
|
|
72
|
+
"""Return metadata for approved public datasets."""
|
|
73
|
+
result = self._request("/api/gena/datasets")
|
|
74
|
+
datasets = result.get("datasets", [])
|
|
75
|
+
if not isinstance(datasets, list):
|
|
76
|
+
raise GenaDatasetsError("Gena returned an unexpected dataset list.")
|
|
77
|
+
return datasets
|
|
78
|
+
|
|
79
|
+
def get_dataset(self, dataset_id: str) -> dict[str, Any]:
|
|
80
|
+
"""Return approved dataset metadata and original file content."""
|
|
81
|
+
if not isinstance(dataset_id, str) or not dataset_id or any(
|
|
82
|
+
character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
|
|
83
|
+
for character in dataset_id
|
|
84
|
+
) or len(dataset_id) > 128:
|
|
85
|
+
raise ValueError("Enter a valid Gena dataset ID.")
|
|
86
|
+
result = self._request("/api/gena/datasets/" + dataset_id)
|
|
87
|
+
dataset = result.get("dataset")
|
|
88
|
+
if not isinstance(dataset, dict):
|
|
89
|
+
raise GenaDatasetsError("Gena returned an unexpected dataset.")
|
|
90
|
+
return dataset
|
|
91
|
+
|
|
92
|
+
def download_dataset(self, dataset_id: str) -> str:
|
|
93
|
+
"""Return the source file contents for a public dataset."""
|
|
94
|
+
return self.get_dataset(dataset_id)["content"]
|
|
95
|
+
|
|
96
|
+
def submit_dataset(
|
|
97
|
+
self,
|
|
98
|
+
dataset: Mapping[str, Any],
|
|
99
|
+
*,
|
|
100
|
+
id_token: str,
|
|
101
|
+
) -> dict[str, Any]:
|
|
102
|
+
"""Submit a dataset privately for moderator review.
|
|
103
|
+
|
|
104
|
+
A successful response means the submission is pending review, not public.
|
|
105
|
+
"""
|
|
106
|
+
if not id_token or not isinstance(id_token, str):
|
|
107
|
+
raise ValueError("Pass a current sign-in token to submit a dataset.")
|
|
108
|
+
if dataset.get("rightsConfirmed") is not True:
|
|
109
|
+
raise ValueError("Confirm that you have the rights to submit this dataset.")
|
|
110
|
+
return self._request(
|
|
111
|
+
"/api/gena/datasets",
|
|
112
|
+
method="POST",
|
|
113
|
+
body=dataset,
|
|
114
|
+
id_token=id_token,
|
|
115
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gena-datasets
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Browse public Gena datasets and submit datasets for review.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: gena,datasets,tuluai,machine-learning
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# `gena-datasets`
|
|
13
|
+
|
|
14
|
+
Python client for browsing public datasets and submitting a dataset to Gena for review.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
python -m pip install gena-datasets
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Read public datasets
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from gena_datasets import GenaDatasetsClient
|
|
26
|
+
|
|
27
|
+
gena = GenaDatasetsClient()
|
|
28
|
+
for item in gena.list_datasets():
|
|
29
|
+
print(item["id"], item["title"], item["license"])
|
|
30
|
+
|
|
31
|
+
dataset = gena.get_dataset("tulueng100-reference")
|
|
32
|
+
print(dataset["content"])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Public catalog access does not require sign-in. `download_dataset(id)` returns the original dataset file contents as text.
|
|
36
|
+
|
|
37
|
+
## Submit a dataset
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
id_token = current_firebase_user.get_id_token()
|
|
41
|
+
submission = gena.submit_dataset(
|
|
42
|
+
{
|
|
43
|
+
"title": "Example collection",
|
|
44
|
+
"description": "A short description of the data and how it was collected.",
|
|
45
|
+
"language": "English",
|
|
46
|
+
"license": "CC BY 4.0",
|
|
47
|
+
"fileName": "examples.jsonl",
|
|
48
|
+
"content": '{"text":"Example"}\n',
|
|
49
|
+
"rightsConfirmed": True,
|
|
50
|
+
},
|
|
51
|
+
id_token=id_token,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
print(submission["datasetId"], submission["status"])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Submissions are private while pending review. A designated Gena moderator must approve a submission before it becomes public. The contributor must have permission to share the data and choose a license that allows publication. Current uploads are limited to 900,000 bytes and `.csv`, `.json`, `.jsonl`, or `.txt` files.
|
|
58
|
+
|
|
59
|
+
## API endpoints
|
|
60
|
+
|
|
61
|
+
- `GET /api/gena/datasets`
|
|
62
|
+
- `GET /api/gena/datasets/{id}`
|
|
63
|
+
- `POST /api/gena/datasets` with a signed-in account token
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/gena_datasets/__init__.py
|
|
5
|
+
src/gena_datasets/py.typed
|
|
6
|
+
src/gena_datasets.egg-info/PKG-INFO
|
|
7
|
+
src/gena_datasets.egg-info/SOURCES.txt
|
|
8
|
+
src/gena_datasets.egg-info/dependency_links.txt
|
|
9
|
+
src/gena_datasets.egg-info/top_level.txt
|
|
10
|
+
tests/test_client.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gena_datasets
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import unittest
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
|
|
8
|
+
|
|
9
|
+
from gena_datasets import GenaDatasetsClient, GenaDatasetsError # noqa: E402
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FakeResponse:
|
|
13
|
+
def __init__(self, body):
|
|
14
|
+
self.body = json.dumps(body).encode("utf-8")
|
|
15
|
+
|
|
16
|
+
def __enter__(self):
|
|
17
|
+
return self
|
|
18
|
+
|
|
19
|
+
def __exit__(self, *_):
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
def read(self):
|
|
23
|
+
return self.body
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GenaDatasetsClientTests(unittest.TestCase):
|
|
27
|
+
def test_lists_public_datasets(self):
|
|
28
|
+
with patch("gena_datasets.urlopen", return_value=FakeResponse({"datasets": [{"id": "sample"}]})) as open_url:
|
|
29
|
+
result = GenaDatasetsClient().list_datasets()
|
|
30
|
+
self.assertEqual(result[0]["id"], "sample")
|
|
31
|
+
self.assertEqual(open_url.call_args.args[0].full_url, "https://tuluai.app/api/gena/datasets")
|
|
32
|
+
|
|
33
|
+
def test_downloads_original_content(self):
|
|
34
|
+
with patch(
|
|
35
|
+
"gena_datasets.urlopen",
|
|
36
|
+
return_value=FakeResponse({"dataset": {"id": "sample", "content": '{"x":1}'}}),
|
|
37
|
+
):
|
|
38
|
+
content = GenaDatasetsClient().download_dataset("sample")
|
|
39
|
+
self.assertEqual(content, '{"x":1}')
|
|
40
|
+
|
|
41
|
+
def test_submits_with_bearer_token_and_requires_rights_confirmation(self):
|
|
42
|
+
response = {"datasetId": "sample", "status": "pending"}
|
|
43
|
+
dataset = {"title": "Sample", "rightsConfirmed": True}
|
|
44
|
+
with patch("gena_datasets.urlopen", return_value=FakeResponse(response)) as open_url:
|
|
45
|
+
result = GenaDatasetsClient().submit_dataset(dataset, id_token="current-token")
|
|
46
|
+
request = open_url.call_args.args[0]
|
|
47
|
+
self.assertEqual(request.get_method(), "POST")
|
|
48
|
+
self.assertEqual(request.get_header("Authorization"), "Bearer current-token")
|
|
49
|
+
self.assertEqual(json.loads(request.data), dataset)
|
|
50
|
+
self.assertEqual(result["status"], "pending")
|
|
51
|
+
|
|
52
|
+
with self.assertRaises(ValueError):
|
|
53
|
+
GenaDatasetsClient().submit_dataset({"rightsConfirmed": False}, id_token="current-token")
|
|
54
|
+
|
|
55
|
+
def test_rejects_invalid_dataset_ids(self):
|
|
56
|
+
with self.assertRaises(ValueError):
|
|
57
|
+
GenaDatasetsClient().get_dataset("bad/id")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
unittest.main()
|