-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from axiomhq/arne/axm-3951-add-annotations-to…
…-axiom-py feat(annotations): Add AnnotationsClient
- Loading branch information
Showing
11 changed files
with
224 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ | |
|
||
from .client import * | ||
from .datasets import * | ||
from .annotations import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
"""This package provides annotation models and methods as well as an AnnotationsClient""" | ||
|
||
import ujson | ||
from logging import Logger | ||
from requests import Session | ||
from typing import List, Dict, Optional | ||
from dataclasses import dataclass, asdict, field | ||
from datetime import datetime, timedelta | ||
from urllib.parse import urlencode | ||
from .util import Util | ||
|
||
|
||
@dataclass | ||
class Annotation: | ||
"""Represents an Axiom annotation""" | ||
|
||
id: str = field(init=False) | ||
datasets: List[str] | ||
time: datetime | ||
endTime: Optional[datetime] | ||
title: Optional[str] | ||
description: Optional[str] | ||
url: Optional[str] | ||
type: str | ||
|
||
|
||
@dataclass | ||
class AnnotationCreateRequest: | ||
"""Request used to create an annotation""" | ||
|
||
datasets: List[str] | ||
time: Optional[datetime] | ||
endTime: Optional[datetime] | ||
title: Optional[str] | ||
description: Optional[str] | ||
url: Optional[str] | ||
type: str | ||
|
||
|
||
@dataclass | ||
class AnnotationUpdateRequest: | ||
"""Request used to update an annotation""" | ||
|
||
datasets: Optional[List[str]] | ||
time: Optional[datetime] | ||
endTime: Optional[datetime] | ||
title: Optional[str] | ||
description: Optional[str] | ||
url: Optional[str] | ||
type: Optional[str] | ||
|
||
|
||
class AnnotationsClient: # pylint: disable=R0903 | ||
"""AnnotationsClient has methods to manipulate annotations.""" | ||
|
||
session: Session | ||
|
||
def __init__(self, session: Session, logger: Logger): | ||
self.session = session | ||
self.logger = logger | ||
|
||
def get(self, id: str) -> Annotation: | ||
"""Get a annotation by id.""" | ||
path = "/v2/annotations/%s" % id | ||
res = self.session.get(path) | ||
decoded_response = res.json() | ||
return Util.from_dict(Annotation, decoded_response) | ||
|
||
def create(self, req: AnnotationCreateRequest) -> Annotation: | ||
"""Create an annotation with the given properties.""" | ||
path = "/v2/annotations" | ||
res = self.session.post(path, data=ujson.dumps(asdict(req))) | ||
annotation = Util.from_dict(Annotation, res.json()) | ||
self.logger.info(f"created new annotation: {annotation.id}") | ||
return annotation | ||
|
||
def list( | ||
self, | ||
datasets: List[str] = [], | ||
start: Optional[datetime] = None, | ||
end: Optional[datetime] = None, | ||
) -> List[Annotation]: | ||
"""List all annotations.""" | ||
query_params = {} | ||
if len(datasets) > 0: | ||
query_params["datasets"] = ",".join(datasets) | ||
if start != None: | ||
query_params["start"] = start.isoformat() | ||
if end != None: | ||
query_params["end"] = end.isoformat() | ||
path = f"/v2/annotations?{urlencode(query_params, doseq=True)}" | ||
|
||
res = self.session.get(path) | ||
|
||
annotations = [] | ||
for record in res.json(): | ||
ds = Util.from_dict(Annotation, record) | ||
annotations.append(ds) | ||
|
||
return annotations | ||
|
||
def update(self, id: str, req: AnnotationUpdateRequest) -> Annotation: | ||
"""Update an annotation with the given properties.""" | ||
path = "/v2/annotations/%s" % id | ||
res = self.session.put(path, data=ujson.dumps(asdict(req))) | ||
annotation = Util.from_dict(Annotation, res.json()) | ||
self.logger.info(f"updated annotation({annotation.id})") | ||
return annotation | ||
|
||
def delete(self, id: str): | ||
"""Deletes an annotation with the given id.""" | ||
path = "/v2/annotations/%s" % id | ||
self.session.delete(path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
"""Logging contains the AxiomHandler and related methods to do with logging.""" | ||
|
||
import time | ||
import atexit | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
"""This module contains helper functions for tests.""" | ||
|
||
import random | ||
from datetime import datetime | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
"""This module contains the tests for the AnnotationsClient.""" | ||
|
||
import os | ||
|
||
import unittest | ||
from typing import List, Dict, Any, Optional | ||
from logging import getLogger | ||
from requests.exceptions import HTTPError | ||
from datetime import timedelta | ||
from .helpers import get_random_name | ||
from axiom import ( | ||
Client, | ||
DatasetCreateRequest, | ||
AnnotationCreateRequest, | ||
AnnotationUpdateRequest, | ||
) | ||
|
||
|
||
class TestAnnotations(unittest.TestCase): | ||
client: Client | ||
dataset_name: str | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
cls.logger = getLogger() | ||
|
||
cls.client = Client( | ||
os.getenv("AXIOM_TOKEN"), | ||
os.getenv("AXIOM_ORG_ID"), | ||
os.getenv("AXIOM_URL"), | ||
) | ||
|
||
# create dataset | ||
cls.dataset_name = get_random_name() | ||
req = DatasetCreateRequest( | ||
name=cls.dataset_name, | ||
description="test_annotations.py (dataset_name)", | ||
) | ||
res = cls.client.datasets.create(req) | ||
|
||
def test_happy_path_crud(self): | ||
"""Test the happy path of creating, reading, updating, and deleting an annotation.""" | ||
# Create annotation | ||
req = AnnotationCreateRequest( | ||
datasets=[self.dataset_name], | ||
type="test", | ||
time=None, | ||
endTime=None, | ||
title=None, | ||
description=None, | ||
url=None, | ||
) | ||
created_annotation = self.client.annotations.create(req) | ||
self.logger.debug(created_annotation) | ||
|
||
# Get annotation | ||
annotation = self.client.annotations.get(created_annotation.id) | ||
self.logger.debug(annotation) | ||
assert annotation.id == created_annotation.id | ||
|
||
# List annotations | ||
annotations = self.client.annotations.list(datasets=[self.dataset_name]) | ||
self.logger.debug(annotations) | ||
assert len(annotations) == 1 | ||
|
||
# Update | ||
newTitle = "Update title" | ||
updateReq = AnnotationUpdateRequest( | ||
datasets=None, | ||
type=None, | ||
time=None, | ||
endTime=None, | ||
title=newTitle, | ||
description=None, | ||
url=None, | ||
) | ||
updated_annotation = self.client.annotations.update(annotation.id, updateReq) | ||
self.logger.debug(updated_annotation) | ||
assert updated_annotation.title == newTitle | ||
|
||
# Delete | ||
self.client.annotations.delete(annotation.id) | ||
|
||
@classmethod | ||
def tearDownClass(cls): | ||
"""Delete datasets""" | ||
cls.client.datasets.delete(cls.dataset_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
"""This module contains the tests for the axiom client.""" | ||
|
||
import os | ||
import unittest | ||
import gzip | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
"""This module contains the tests for the DatasetsClient.""" | ||
|
||
import os | ||
|
||
import unittest | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
"""This module contains test for the logging Handler.""" | ||
|
||
import os | ||
import logging | ||
import unittest | ||
|