Skip to content

Commit e15572b

Browse files
committed
Initial commit.
0 parents  commit e15572b

File tree

17 files changed

+1248
-0
lines changed

17 files changed

+1248
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist/
2+
__pycache__/
3+
.pytest_cache/

CITATIONS.cff

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
cff-version: 1.2.0
2+
title: pyBrAPI
3+
message: >-
4+
If you use this software, please cite it using the
5+
metadata from this file.
6+
type: software
7+
authors:
8+
- given-names: Manuel
9+
family-names: Feser
10+
11+
name-particle: Manuel
12+
affiliation: >-
13+
Leibniz Institute of Plant Genetics and Crop Plant
14+
Research
15+
orcid: 'https://orcid.org/0000-0001-6546-1818'
16+
repository-code: 'https://github.com/IPK-BIT/pyBrapi'
17+
abstract: >-
18+
pyBrAPI is a Python package that provides convenient
19+
access to data from BrAPI servers. It simplifies
20+
interacting with BrAPI endpoints by handling REST requests
21+
and responses, and returns data typed into Python data
22+
models.
23+
keywords:
24+
- BrAPI
25+
- python
26+
license: MIT
27+
version: 0.1.0

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Manuel Feser <[email protected]>
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.

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# pyBrAPI
2+
3+
## Project Overview
4+
pyBrAPI is a Python package that provides convenient access to data from BrAPI servers. It simplifies interacting with BrAPI endpoints by handling REST requests and responses, and returns data typed into data models.
5+
6+
## Setup and Installation
7+
Download the release from this Github repository. Then install the package using `pip`:
8+
9+
```
10+
pip install dist/pybrapi-0.1.0-py3-none-any.whl
11+
```
12+
13+
## Usage Instructions
14+
### Connect to server
15+
```py
16+
from pybrapi import BrAPI
17+
18+
server = BrAPI('https://test-server.brapi.org/brapi/v2/')
19+
```
20+
21+
### Request Germplasm
22+
```py
23+
response = server.get_germplasm(germplasmDbId="germplasm1")
24+
```
25+
26+
Similar functions are provided for:
27+
- trials
28+
- studies
29+
- observations
30+
- observation units
31+
- observation variables
32+
33+
### Request Observations Tables
34+
Additionally for observations, users can receive the data in a pandas data frame.
35+
```py
36+
df = server.get_observations_as_dataframe(observationVariableDbId="variable1")
37+
```
38+
39+
40+
## Features
41+
- Simplified interface for querying BrAPI endpoints
42+
- Typed data models for returned data
43+
- Handles authentication and connection management
44+
- Supports various BrAPI operations like retrieving germplasm, observations, trials, etc.
45+
46+
## License
47+
- The project is licensed under [MIT License](LICENSE)
48+
49+
## Additional Resources
50+
- [BrAPI](https://brapi.org)
51+
- [BrAPI Specification](https://brapi.org/specification)

poetry.lock

Lines changed: 659 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pybrapi/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from dataclasses import dataclass
2+
from pybrapi.authentication import AbstractAuth
3+
from pybrapi.modules import core, phenotyping, germplasm
4+
5+
@dataclass
6+
class BrAPI:
7+
base_url: str = "https://test-server.brapi.org/brapi/v2"
8+
auth: AbstractAuth|None = None
9+
10+
__core__: core.Core = core.Core()
11+
__germplasm__: germplasm.Germplasm = germplasm.Germplasm()
12+
__phenotyping__: phenotyping.Phenotyping = phenotyping.Phenotyping()
13+
14+
def get_germplasm(self, **kwargs):
15+
return self.__germplasm__.get_germplasm(self.base_url, self.auth, **kwargs)
16+
17+
def get_observations(self, **kwargs):
18+
return self.__phenotyping__.get_observations(self.base_url, self.auth, **kwargs)
19+
20+
def get_observations_as_dataframe(self, **kwargs):
21+
return self.__phenotyping__.get_observations_as_dataframe(self.base_url, self.auth, **kwargs)
22+
23+
def get_observation_units(self, **kwargs):
24+
return self.__phenotyping__.get_observation_units(self.base_url, self.auth, **kwargs)
25+
26+
def get_observation_variables(self, **kwargs):
27+
return self.__phenotyping__.get_observation_variables(self.base_url, self.auth, **kwargs)
28+
29+
def get_studies(self, **kwargs):
30+
return self.__core__.get_studies(self.base_url, self.auth, **kwargs)
31+
32+
def get_trials(self, **kwargs):
33+
return self.__core__.get_trials(self.base_url, self.auth, **kwargs)
34+

pybrapi/__utils__/__collector__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import requests
2+
3+
def __collect_results__(url, headers, params):
4+
if "pageSize" not in params:
5+
params["pageSize"] = 1000
6+
if "page" not in params:
7+
params["page"] = 0
8+
9+
response = __get_page__(url, headers, params)
10+
# print(f'collecting page {params["page"]+1}/{response["metadata"]["pagination"]["totalPages"]}')
11+
if "headerRow" in response["result"]:
12+
result = response["result"]
13+
while params["page"] < response["metadata"]["pagination"]["totalPages"]-1:
14+
params["page"] += 1
15+
result["data"].extend(__get_page__(url, headers, params)["result"]["data"])
16+
# print(f'collecting page {params["page"]+1}/{response["metadata"]["pagination"]["totalPages"]}')
17+
elif "data" in response["result"]:
18+
result: list = response["result"]["data"]
19+
while params["page"] < response["metadata"]["pagination"]["totalPages"]-1:
20+
params["page"] += 1
21+
result.extend(__get_page__(url, headers, params)["result"]["data"])
22+
else:
23+
result = response["result"]
24+
return result
25+
26+
def __get_page__(url, headers, params):
27+
response = requests.get(url, headers=headers, params=params)
28+
response.raise_for_status()
29+
return response.json()

pybrapi/authentication.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from dataclasses import dataclass
2+
from base64 import b64encode
3+
import requests
4+
5+
@dataclass
6+
class AbstractAuth:
7+
def get_auth_string(self):
8+
raise NotImplementedError("get_auth_string not implemented")
9+
10+
@dataclass
11+
class BasicAuth(AbstractAuth):
12+
username: str
13+
password: str
14+
15+
def get_auth_string(self):
16+
return f"Basic {b64encode(f'{self.username}:{self.password}'.encode()).decode()}"
17+
18+
@dataclass
19+
class OIDCAuth(AbstractAuth):
20+
oidc_url: str
21+
oidc_details: dict
22+
__token__: str = None
23+
__refresh_token__: str = None
24+
25+
def __init__(self, oidc_url: str, oidc_details: dict):
26+
self.oidc_url = oidc_url
27+
self.oidc_details = oidc_details
28+
self.__token__ = self.get_token()
29+
30+
def get_token(self):
31+
response = requests.post(f"{self.oidc_url}/token", data=self.oidc_details)
32+
response.raise_for_status()
33+
self.__refresh_token__ = response.json()["refresh_token"]
34+
return response.json()["access_token"]
35+
36+
def get_auth_string(self):
37+
return f"Bearer {self.__token__}"
38+
39+
def refresh(self):
40+
self.__token__ = self.get_token()

pybrapi/models/core.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from pydantic import BaseModel
2+
from pybrapi.models.subclasses import Contact, DataLink, DatasetAuthorship, EnvironmentalParameter, ExperimentalDesign, ExternalReference, GrowthFacility, LastUpdate, ObservationUnitHierarchyLevel, Publication
3+
4+
class Trial(BaseModel):
5+
active: bool|None = None
6+
additionalInfo: dict|None = None
7+
commonCropName: str|None = None
8+
contacts: list[Contact]|None = None
9+
datasetAuthorships: list[DatasetAuthorship]|None = None
10+
documentationURL: str|None = None
11+
endDate: str|None = None
12+
externalReferences: list[ExternalReference]|None = None
13+
programDbId: str|None = None
14+
programName: str|None = None
15+
publications: list[Publication]|None = None
16+
startDate: str|None = None
17+
trialDbId: str
18+
trialDescription: str|None = None
19+
trialName: str
20+
trialPUI: str|None = None
21+
22+
class Study(BaseModel):
23+
active: bool|None = None
24+
additionalInfo: dict|None = None
25+
commonCropName: str|None = None
26+
contacts: list[Contact]|None = None
27+
culturalPractices: str|None = None
28+
dataLinks: list[DataLink]|None = None
29+
documentationURL: str|None = None
30+
endDate: str|None = None
31+
environmentParameters: list[EnvironmentalParameter]|None = None
32+
experimentalDesign: ExperimentalDesign|None = None
33+
externalReferences: list[ExternalReference]|None = None
34+
growthFacility: GrowthFacility|None = None
35+
lastUpdate: LastUpdate|None = None
36+
license: str|None = None
37+
locationDbId: str|None = None
38+
locationName: str|None = None
39+
observationLevels: list[ObservationUnitHierarchyLevel]|None = None
40+
observationUnitsDescription: str|None = None
41+
observationVariableDbIds: list[str]|None = None
42+
seasons: list[str]|None = None
43+
startDate: str|None = None
44+
studyCode: str|None = None
45+
studyDbId: str
46+
studyDescription: str|None = None
47+
studyName: str
48+
studyPUI: str|None = None
49+
studyType: str|None = None
50+
trialDbId: str|None = None
51+
trialName: str|None = None

pybrapi/models/germplasm.py

Whitespace-only changes.

0 commit comments

Comments
 (0)