Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/AAFC-BICoE/dina-py into 3484…
Browse files Browse the repository at this point in the history
…1-Set-Date-Orginal-Version-for-file-upload
  • Loading branch information
johnphan96 committed Sep 4, 2024
2 parents 9f0a4c3 + 37ad1d7 commit 0523d3c
Show file tree
Hide file tree
Showing 25 changed files with 1,010 additions and 196 deletions.
219 changes: 107 additions & 112 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,84 @@ Python access to the DINA API

## Current Features

* JSON-API serializer and deserializer with Marshmallow JSON-API
* GET request for a Person (PersonAPI)
#### JSON-API serializer and deserializer with Marshmallow JSON-API
- Currently supported schemas:
- Collecting Event
- Storage Unit Usage
- Person
- Material Sample
- Metadata
- Storage Unit
- Form Template
- Split Configuration
#### DINA APIs
- Currently supported APIs:
- Agent API
- Person
- Collection API
- CollectingEvent
- StorageUnit
- StorageUnitUsage
- FormTemplate
- SplitConfiguration
- MaterialSample
- Organism
- Object Store API
- Metadata
- UploadFile
- SeqDB API
- PCR Batch
- PCR Batch Item
- SEQ Reaction

#### DINA_API_CLIENT
#### Currently supported operations:
**upload_file:**
- metavar="<file_path> : (str) = Path to the file to be uploaded.",
- help="Upload a file to Object Store. Argument: file_path"

**upload_dir:**
- metavar="<dir_path> : (str) = Path to the directory to be uploaded.",
- help="Upload all files in a directory to Object Store",

**verbose:**
- help="Verbosity of logs. Primarily for debugging.",
- action="store_true",

**create_metadatas:**
- metavar="<dir_path> : (str) = Path to the directory to be uploaded.",
- help="Upload all files in a directory to Object Store and create metadatas according to constants defined in ./dina-api-config.yml",

**create_form_template:**
- metavar="<file_path> : (str) = Path to the file to be parsed and created.",
- help="Create a form template according to specs defined in a yaml file such as ./form-template-sample.yml",

**create_split_configuration:**
- metavar="<file_path> : (str) = Path to the file to be parsed and created.",
- help="Create a split configuration according to specs defined in a yaml file such as ./split-configuration-sample.yml",

#### TEST COVERAGE
- APIs (Unit Test with Magic Mock):
- Collection API
- Collecting Event
- Managed Attributes
- Material Sample
- Organism
- Object Store API
- Metadata
- Agent API
- Person
- Schemas:
- Collecting Event
- Form Template
- Managed Attribute
- Material Sample
- Metadata
- Person
- Split Configuration
- Storage Unit Usage

## Installation Instructions

### Dina-scripts usage

From inside of the dina-scripts docker container, the dina-py library can be installed using the
GitHub branch you wish to install:

```bash
pip install "https://github.com/AAFC-BICoE/dina-py/archive/refs/heads/dev.zip"
```

Running the command above will install the `dev` branch. Any branch name can be used.

### Local Install

Before installing, it's recommended to use a virtual environment in python but not required. create
Expand All @@ -30,17 +92,11 @@ sudo apt install python3.10-venv
python3 -m venv env
source env/bin/activate
```

Install the library and all required dependencies:
From inside of the dina-py root folder, install the library and all required dependencies:

```bash
python3 -m pip install .
```

Then it can be imported using:

```py
from dinapy.api.agentapi.personapi import PersonAPI
pip install -e .
pip install -r requirements.txt
```

The username and password are set via environment variables. This can be done via the command line:
Expand All @@ -57,100 +113,39 @@ os.environ["keycloak_username"] = "username"
os.environ["keycloak_password"] = "password"
```

## How to extend functionality

* To extend functionality, create subclasses from DinaAPI and extend the base_url attribute with the full URL for the API call required
* Then create new methods for GET, POST, PATCH, etc. and use the base DinaAPI request functions.
* Have the methods return objects of the entity class in the future
* Create a Schema using Marshmallow JSON-API for the entity and use it for serialization and deserialization.
* Try to create tests.
* Make a copy of **keycloak-config-sample.yml** and rename to **keycloak-config.yml** in the root of dina-py directory, open **keycloak-config.yml** using Notepad
* In **keycloak-config.yml**, change **url, keycloak_username, keycloak_password** as needed
* Make a copy of **dina-api-config-sample.yml** and rename to **dina-api-config.yml** in the root of dina-py directory, open **dina-api-config.yml** using Notepad

## Example API Usage

In code:
Sample usage of DINA_API_CLIENT:
```py
from dinapy.api.agentapi.personapi import PersonAPI

// GET request
pAPI = PersonAPI("keycloak config path")
person = pAPI.find("my uuid") // returns deserialized JSON as dict

serialized_data = person_schema.dump(person) // serialize dict back to valid JSON
(.venv) C:\Users\<your_account>\dina-py> python .\dinapy\client\dina_api_client.py -upload_dir '<object_upload_dir>'
```

Deserialized data:
```json
{
"id": "bfa3c68b-8e13-4295-8e25-47dbe041cb64",
"attributes": {
"displayName": "testBob",
"email": "[email protected]",
"createdBy": "cnc-su",
"createdOn": datetime.datetime(
2023, 2, 20, 16, 18, 10, 688627, tzinfo=datetime.timezone.utc
),
"givenNames": "Bob",
"familyNames": "Builder",
"aliases": ["Yes we can"],
"webpage": None,
"remarks": None,
},
"meta": {"totalResourceCount": 1, "moduleVersion": "0.24"},
}
```

Serialized Data:
```json
{
"data": {
"type": "person",
"id": "bfa3c68b-8e13-4295-8e25-47dbe041cb64",
"attributes": {
"displayName": "testBob",
"email": "[email protected]",
"createdBy": "cnc-su",
"createdOn": "2023-02-20T16:18:10.688627+00:00",
"givenNames": "Bob",
"familyNames": "Builder",
"aliases": ["Yes we can"],
"webpage": None,
"remarks": None,
},
"relationships": {
"identifiers": {
"links": {
"self": "/api/v1/person/bfa3c68b-8e13-4295-8e25-47dbe041cb64/relationships/identifiers",
"related": "/api/v1/person/bfa3c68b-8e13-4295-8e25-47dbe041cb64/identifiers",
}
},
"organizations": {
"links": {
"self": "/api/v1/person/bfa3c68b-8e13-4295-8e25-47dbe041cb64/relationships/organizations",
"related": "/api/v1/person/bfa3c68b-8e13-4295-8e25-47dbe041cb64/organizations",
}
},
},
"links": {"self": "/api/v1/person/bfa3c68b-8e13-4295-8e25-47dbe041cb64"},
},
"links": {"self": "/api/v1/person/bfa3c68b-8e13-4295-8e25-47dbe041cb64"},
"meta": {"totalResourceCount": 1, "moduleVersion": "0.24"},
}
* Or run the following command to upload a specific file:
```py
(.venv) C:\Users\<your_account>\dina-py> python .\dinapy\client\dina_api_client.py -upload_file '<object_upload_dir>/<file_name>'
```

## Notes

* Only the find() method for PersonAPI has been tested to work. The other requests (POST, PATCH, DELETE) have not been tested and should be re-implemented or modified.
* Furthermore, the PersonSchema serializes and deserialized correctly in the context of the find() method.
* Keycloak username and password must be set as environmental variables (keycloak_username and keycloak_password). Can be set using os.environ if needed.

## Todo
## Example API Usage

* Develop models in order to have an object returned rather than a dict for the request return types.
* A way to re-use tokens when making different API calls.
* Implement and test the other request types for Person.
* Add support for other Dina entities.
In code:
```py

## Tests
def test_create_delete_entity(self):
schema = MaterialSampleSchema()
material_sample_api = MaterialSampleAPI()
try:
material_sample_attributes = MaterialSampleAttributesDTOBuilder().group("aafc").materialSampleName("test").materialSampleType("WHOLE_ORGANISM")\
.build()
material_sample = MaterialSampleDTOBuilder().attributes(material_sample_attributes).build()
serialized_material_sample = schema.dump(material_sample)
pp = pprint.PrettyPrinter(indent=0)
pp.pprint(serialized_material_sample)
response = material_sample_api.create_entity(serialized_material_sample)
id = response.json()['data']['id']
if response.status_code == 201:
response = material_sample_api.remove_entity(id)
self.assertEqual(response.status_code,204)
except ValidationError as e:
self.fail(f"Validation failed with error: {e.messages}")

* 2 tests only, both for PersonSchema, testing deserialization only
* Can run tests directly from test file
19 changes: 19 additions & 0 deletions dinapy/apis/collectionapi/formtemplateapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file contains the FormTemplateAPI class used for interacting with the Form Template endpoint of the Collection API.
from .collectionapi import CollectionModuleApi

class FormTemplateAPI(CollectionModuleApi):
"""
Class for handling form template related DINA API requests.
"""

def __init__(self, config_path: str = None, base_url: str = None) -> None:
"""
Parameters:
config_path (str, optional): Path to a config file (default: None).
base_url (str, optional): URL to the URL to perform the API requests against. If not
provided then local deployment URL is used. Should end with a forward slash.
"""
super().__init__(config_path, base_url)
self.base_url += "form-template"


19 changes: 19 additions & 0 deletions dinapy/apis/collectionapi/splitconfigurationapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file contains the SplitConfigurationAPI class used for interacting with the Split Configuration endpoint of the Collection API.
from .collectionapi import CollectionModuleApi

class SplitConfigurationAPI(CollectionModuleApi):
"""
Class for handling split configuration related DINA API requests.
"""

def __init__(self, config_path: str = None, base_url: str = None) -> None:
"""
Parameters:
config_path (str, optional): Path to a config file (default: None).
base_url (str, optional): URL to the URL to perform the API requests against. If not
provided then local deployment URL is used. Should end with a forward slash.
"""
super().__init__(config_path, base_url)
self.base_url += "split-configuration"


Loading

0 comments on commit 0523d3c

Please sign in to comment.