Skip to content

Commit

Permalink
Pycap 28 allow objects client api (#29)
Browse files Browse the repository at this point in the history
* PYCAP-28: Modify adapter's interface signature

* PYCAP-28: Modify http adapter to comply with the new signatures

* PYCAP-28: Refactor _get_object_id

* PYCAP-28: Modify http adapter to comply with the new signatures

* PYCAP-28: Add tests for the adapter methods

* PYCAP-28: Add tests for the get_object_id method

* PYCAP-28: Remove 'sugar' file from coverage

* PYCAP-28: Add docs

* PYCAP-28: Fix linting

* Bump version: 0.0.4 → 0.1.0 [ci skip]
  • Loading branch information
JavierLuna committed Jan 18, 2020
1 parent 2479605 commit 73f5e41
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 71 deletions.
24 changes: 20 additions & 4 deletions docs/api/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Pycaprio uses the `Annotation` object to model INCEpTION's documents, and has th
### List annotations
Lists all the annotations in an INCEpTION's document.

You can provide a `Project` instance instead of a `project_id` as well.
You can provide a `Document` instance instead of a `document_id` as well.

Example:
```python
annotations = client.api.annotations(1, 4) # Annotations in document #4 in project #1
Expand All @@ -20,9 +23,13 @@ print(annotations) # [<Annotation by test-user (Project: 1, Document: 4)>, <Anno

### Download annotation
Downloads a document's annotation content.

You can specify the annotation's format via `annotation_format` (defaults to `webanno`).

Example:
You can provide a `Project` instance instead of a `project_id` as well.
You can provide a `Document` instance instead of a `document_id` as well.

Example:

```python
from pycaprio.mappings import InceptionFormat
Expand All @@ -34,19 +41,28 @@ with open("downloaded_annotation", 'wb') as annotation_file:

### Upload annotation
Uploads an annotation to a document in INCEpTION. It requires the Id of the project, the Id of the document, the annotator's username and the annotation's content (io stream).

You can specify the annotation's format via `annotation_format` (defaults to `webanno`) and its state via `annotation_state` (defaults to `NEW`).


You can provide a `Project` instance instead of a `project_id` as well.
You can provide a `Document` instance instead of a `document_id` as well.


Example:

```python
from pycaprio.mappings import InceptionFormat, AnnotationState
with open("annotation") as annotation_file:
with open("annotation", 'rb') as annotation_file:
new_annotation = client.api.create_annotation(1, 4, 'leonardo-dicaprio', annotation_file, annotation_format=InceptionFormat.WEBANNO, annotation_state=AnnotationState.ANNOTATION_IN_PROGRESS)
print(new_annotation) # <Annotation by leonardo-dicaprio (Project: 1, Document: 4)>
```

### Delete annotation
Deletes a document from a project.
Deletes an annotation from a project.

You can provide a `Project` instance instead of a `project_id` as well.
You can provide a `Document` instance instead of a `document_id` as well.


Example:

Expand Down
21 changes: 17 additions & 4 deletions docs/api/documents.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,58 @@ Pycaprio uses the `Document` object to model INCEpTION's documents, and has the
### List documents
Lists all the documents in a project that are in INCEpTION.

You can provide a `Project` instance instead of a `project_id` as well.

Example:
```python
documents = client.api.documents(1) # In project #1
documents = client.api.documents(project_one) # In project #1
print(documents) # [<Document #4: Doc name (Project: 1)>, <Document #5: Doc name 2 (Project: 1)>]
```

### Download document
Downloads a document's content.
You can specify the annotation's format via `document_format` (defaults to `webanno`).

Example:
You can provide a `Project` instance instead of a `project_id` as well.
You can provide a `Document` instance instead of a `document_id` as well.

Example:

```python
from pycaprio.mappings import InceptionFormat
document_content = client.api.document(1, 4, document_format=InceptionFormat.WEBANNO) # Downloads document 4 from project 1
document_content = client.api.document(project, document, document_format=InceptionFormat.WEBANNO) # Downloads document 4 from project 1

with open("downloaded_document", 'wb') as document_file:
document_file.write(document_content)
```

### Upload document
Uploads a document to a project in INCEpTION. It requires the Id of the project, the name of the document and the content of it (io stream).

You can specify the document's format via `document_format` (defaults to `webanno`).
You can specify the document's state via `document_state` (defaults to `NEW`).


You can provide a `Project` instance instead of a `project_id` as well.

Example:

```python
from pycaprio.mappings import InceptionFormat, DocumentState
with open("document", 'rb') as document_file:
new_document = client.api.create_document(1, "Test document name", document_file, document_format=InceptionFormat.WEBANNO, document_state=DocumentState.IN_PROGRESS)
new_document = client.api.create_document(project, "Test document name", document_file, document_format=InceptionFormat.WEBANNO, document_state=DocumentState.IN_PROGRESS)
print(new_document) # <Document #5: Test document name (Project: 1)>
```

### Delete document
Deletes a document from a project.

You can provide a `Project` instance instead of a `project_id` as well.
You can provide a `Document` instance instead of a `document_id` as well.

Example:

```python
client.api.delete_document(1, 4) # Deletes document #4 from project #1
client.api.delete_document(project, document) # Deletes document #4 from project #1
```
19 changes: 13 additions & 6 deletions docs/api/projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ print(projects) # [<Project #1: Project name>, <Project #2: Another project>]
```

### Fetch project
Fetches a project by its `project_id`.
Fetches a project by its `id`.

Example:
You can provide a `Project` instance instead of an `int` as well.

Example:

```python
project = client.api.project(1)
project = client.api.project(1) # Or updated_project = client.api.project(project)
print(project) # <Project #1: Project name>
```

Expand All @@ -37,23 +39,28 @@ print(new_project) # <Project #3: New project name>
```

### Delete project
Deletes a project.
Deletes a project by its `id`.

You can provide a `Project` instance instead of an `int` as well.

Example:

```python
client.api.delete_project(3)
client.api.delete_project(project)
```

### Export project
Exports a project into a zip. Pycaprio returns the zip's content in bytes to allow flexibility in use/storage.
You can specify the export file format using the `format` parameter. By default, it uses the `webanno` format.
You can specify the export file format using the `format` parameter. By default, it uses the `webanno` format.

You can provide a `Project` instance instead of an `int` as well.

Example:

```python
from pycaprio.mappings import InceptionFormat
content = client.api.export_project(1, project_format=InceptionFormat.XMI) # type(content) is bytes
content = client.api.export_project(project, project_format=InceptionFormat.XMI) # type(content) is bytes
with open("exported_project.zip", 'wb') as zip_file:
zip_file.write(content)
```
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ projects = client.api.projects()
# Export all projects in XMI format
from pycaprio.mappings import InceptionFormat
for project in projects:
zip_content = client.api.export_project(project.project_id, format=InceptionFormat.XMI)
zip_content = client.api.export_project(project, format=InceptionFormat.XMI)
with open(f"{project.project_name}.zip", 'wb') as zip_file:
zip_file.write(zip_content)
```
2 changes: 1 addition & 1 deletion pycaprio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# flake8: noqa
from pycaprio.core.pycaprio import Pycaprio

__version__ = '0.0.4'
__version__ = '0.1.0'
62 changes: 48 additions & 14 deletions pycaprio/core/adapters/http_adapter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import IO
from typing import IO, Union
from typing import List
from typing import Optional

Expand Down Expand Up @@ -30,23 +30,30 @@ def projects(self) -> List[Project]:
response = self.client.get('/projects', allowed_statuses=(200,))
return ProjectSchema().load(response.json()['body'], many=True)

def project(self, project_id: int) -> Project:
def project(self, project: Union[Project, int]) -> Project:
project_id = self._get_object_id(project)
response = self.client.get(f'/projects/{project_id}')
return ProjectSchema().load(response.json()['body'], many=False)

def documents(self, project_id: int) -> List[Document]:
def documents(self, project: Union[Project, int]) -> List[Document]:
project_id = self._get_object_id(project)
response = self.client.get(f'/projects/{project_id}/documents', allowed_statuses=(200,))
document_list = DocumentSchema().load(response.json()['body'], many=True)
for document in document_list:
document.project_id = project_id
return document_list

def document(self, project_id: int, document_id: int, document_format: str = InceptionFormat.DEFAULT) -> bytes:
def document(self, project: Union[Project, int], document: Union[Document, int],
document_format: str = InceptionFormat.DEFAULT) -> bytes:
project_id = self._get_object_id(project)
document_id = self._get_object_id(document)
response = self.client.get(f'/projects/{project_id}/documents/{document_id}', allowed_statuses=(200,),
params={'format': document_format})
return response.content

def annotations(self, project_id: int, document_id: int) -> List[Annotation]:
def annotations(self, project: Union[Project, int], document: Union[Document, int]) -> List[Annotation]:
project_id = self._get_object_id(project)
document_id = self._get_object_id(document)
response = self.client.get(f'/projects/{project_id}/documents/{document_id}/annotations',
allowed_statuses=(200,))
annotation_list = AnnotationSchema().load(response.json()['body'], many=True)
Expand All @@ -55,8 +62,10 @@ def annotations(self, project_id: int, document_id: int) -> List[Annotation]:
annotation.document_id = document_id
return annotation_list

def annotation(self, project_id: int, document_id: int, user_name: str,
def annotation(self, project: Union[Project, int], document: Union[Document, int], user_name: str,
annotation_format: str = InceptionFormat.DEFAULT) -> bytes:
project_id = self._get_object_id(project)
document_id = self._get_object_id(document)
response = self.client.get(f'/projects/{project_id}/documents/{document_id}/annotations/{user_name}',
allowed_statuses=(200,), params={'format': annotation_format})
return response.content
Expand All @@ -67,8 +76,10 @@ def create_project(self, project_name: str, creator_name: Optional[str] = None)
allowed_statuses=(201,))
return ProjectSchema().load(response.json()['body'])

def create_document(self, project_id: int, document_name: str, content: IO,
document_format: str = InceptionFormat.DEFAULT, document_state: str = DocumentState.DEFAULT):
def create_document(self, project: Union[Project, int], document_name: str, content: IO,
document_format: str = InceptionFormat.DEFAULT,
document_state: str = DocumentState.DEFAULT) -> Document:
project_id = self._get_object_id(project)
response = self.client.post(f"/projects/{project_id}/documents", form_data={"name": document_name,
"format": document_format,
"state": document_state},
Expand All @@ -78,9 +89,12 @@ def create_document(self, project_id: int, document_name: str, content: IO,
document.project_id = project_id
return document

def create_annotation(self, project_id: int, document_id: int, user_name: str, content: IO,
def create_annotation(self, project: Union[Project, int], document: Union[Document, int], user_name: str,
content: IO,
annotation_format: str = InceptionFormat.DEFAULT,
annotation_state: str = AnnotationState.DEFAULT):
annotation_state: str = AnnotationState.DEFAULT) -> Annotation:
project_id = self._get_object_id(project)
document_id = self._get_object_id(document)
response = self.client.post(f"/projects/{project_id}/documents/{document_id}/annotations/{user_name}",
form_data={'format': annotation_format, 'state': annotation_state},
files={"content": ('test/path', content)},
Expand All @@ -90,20 +104,26 @@ def create_annotation(self, project_id: int, document_id: int, user_name: str, c
annotation.document_id = document_id
return annotation

def delete_project(self, project_id: int) -> bool:
def delete_project(self, project: Union[Project, int]) -> bool:
project_id = self._get_object_id(project)
self.client.delete(f'/projects/{project_id}', allowed_statuses=(204, 200,))
return True

def delete_document(self, project_id: int, document_id: int) -> bool:
def delete_document(self, project: Union[Project, int], document: Union[Document, int]) -> bool:
project_id = self._get_object_id(project)
document_id = self._get_object_id(document)
self.client.delete(f'/projects/{project_id}/documents/{document_id}', allowed_statuses=(204, 200))
return True

def delete_annotation(self, project_id: int, document_id: int, user_name: str):
def delete_annotation(self, project: Union[Project, int], document: Union[Document, int], user_name: str) -> bool:
project_id = self._get_object_id(project)
document_id = self._get_object_id(document)
self.client.delete(f'/projects/{project_id}/documents/{document_id}/annotations/{user_name}',
allowed_statuses=(204, 200))
return True

def export_project(self, project_id: int, project_format: str = InceptionFormat.DEFAULT) -> bytes:
def export_project(self, project: Union[Project, int], project_format: str = InceptionFormat.DEFAULT) -> bytes:
project_id = self._get_object_id(project)
response = self.client.get(f"/projects/{project_id}/export.zip", allowed_statuses=(200,),
params={'format': project_format})
return response.content
Expand All @@ -112,3 +132,17 @@ def import_project(self, zip_stream: IO) -> Project:
response = self.client.post("/projects/import", files={"file": ('test/path', zip_stream)},
allowed_statuses=(201, 200))
return ProjectSchema().load(response.json()['body'], many=False)

@staticmethod
def _get_object_id(o: Union[int, Project, Document, Annotation]) -> int:
object_id_mappings = {
Project: lambda p: p.project_id,
Document: lambda d: d.document_id,
Annotation: lambda a: a.user_name
}
for object_class, mapper in object_id_mappings.items():
if isinstance(o, object_class):
return mapper(o)

# If it is an unknown type, just return it.
return o
Loading

0 comments on commit 73f5e41

Please sign in to comment.