Skip to content

Commit

Permalink
[PY-544][external] Item query & Item ID query sorting functionality +…
Browse files Browse the repository at this point in the history
… some linting (#764)

* Item query * Item ID query sorting functionality + some linting

* Typing

* Fixed typing issue
  • Loading branch information
JBWilkie authored Jan 8, 2024
1 parent b3e9317 commit 8028311
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 28 deletions.
18 changes: 18 additions & 0 deletions darwin/future/data_objects/sorting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import Literal, Optional

from pydantic import BaseModel, Field, root_validator


class SortingMethods(BaseModel):
accuracy: Optional[Literal["asc", "desc"]] = Field(None)
byte_size: Optional[Literal["asc", "desc"]] = Field(None)
id: Optional[Literal["asc", "desc"]] = Field(None)
map: Optional[Literal["asc", "desc"]] = Field(None)
name: Optional[Literal["asc", "desc"]] = Field(None)
priority: Optional[Literal["asc", "desc"]] = Field(None)
updated_at: Optional[Literal["asc", "desc"]] = Field(None)

@root_validator(pre=True)
def check_at_least_one_field(cls, values):
assert any(value is not None for value in values.values())
return values
17 changes: 16 additions & 1 deletion darwin/future/meta/queries/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
from darwin.future.core.items.tag_items import tag_items
from darwin.future.core.items.untag_items import untag_items
from darwin.future.core.types.common import QueryString
from darwin.future.core.types.query import PaginatedQuery
from darwin.future.core.types.query import PaginatedQuery, QueryFilter
from darwin.future.data_objects.item import ItemLayout
from darwin.future.data_objects.sorting import SortingMethods
from darwin.future.data_objects.workflow import WFStageCore
from darwin.future.exceptions import BadRequest
from darwin.future.meta.objects.item import Item
Expand Down Expand Up @@ -60,6 +61,20 @@ def _collect(self) -> Dict[int, Item]:
}
return items

def sort(self, **kwargs: str) -> ItemQuery:
valid_values = {"asc", "desc"}
for value in kwargs.values():
if value not in valid_values:
raise ValueError(
f"Invalid sort value: {value}. Must be one of {valid_values}."
)
sorting_methods = SortingMethods(**kwargs) # type: ignore
for key, value in sorting_methods.dict().items():
if value is not None:
filter = QueryFilter(name=f"sort[{key}]", param=value)
self.filters.append(filter)
return self

def delete(self) -> None:
if "team_slug" not in self.meta_params:
raise ValueError("Must specify team_slug to query items")
Expand Down
17 changes: 16 additions & 1 deletion darwin/future/meta/queries/item_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from darwin.future.core.items.get import get_item_ids
from darwin.future.core.types.common import QueryString
from darwin.future.core.types.query import PaginatedQuery
from darwin.future.core.types.query import PaginatedQuery, QueryFilter
from darwin.future.data_objects.sorting import SortingMethods
from darwin.future.meta.objects.v7_id import V7ID


Expand Down Expand Up @@ -39,3 +40,17 @@ def _collect(self) -> Dict[int, V7ID]:
for i, uuid in enumerate(uuids)
}
return results

def sort(self, **kwargs: str) -> "ItemIDQuery":
valid_values = {"asc", "desc"}
for value in kwargs.values():
if value not in valid_values:
raise ValueError(
f"Invalid sort value: {value}. Must be one of {valid_values}."
)
sorting_methods = SortingMethods(**kwargs) # type: ignore
for key, value in sorting_methods.dict().items():
if value is not None:
filter = QueryFilter(name=f"sort[{key}]", param=value)
self.filters.append(filter)
return self
25 changes: 25 additions & 0 deletions darwin/future/tests/data_objects/test_sorting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest
from pydantic import ValidationError

from darwin.future.data_objects.sorting import SortingMethods


def test_sorting_methods_all_fields_none():
with pytest.raises(ValidationError):
SortingMethods()


def test_sorting_methods_one_field_set():
sorting = SortingMethods(accuracy="asc")
assert sorting.accuracy == "asc"


def test_sorting_methods_multiple_fields_set():
sorting = SortingMethods(accuracy="asc", byte_size="desc")
assert sorting.accuracy == "asc"
assert sorting.byte_size == "desc"


def test_sorting_methods_invalid_value():
with pytest.raises(ValidationError):
SortingMethods(accuracy="invalid")
12 changes: 12 additions & 0 deletions darwin/future/tests/meta/queries/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,15 @@ def test_untag_bad_request(
item_query.untag(tag_id)
(msg,) = excinfo.value.args
assert msg == "tag_id must be an integer, got <class 'str'>"


def test_sort_method(base_client: ClientCore):
item_query = ItemQuery(
base_client, meta_params={"dataset_id": 0000, "team_slug": "test_team"}
)

item_query.sort(accuracy="desc", byte_size="asc")

assert len(item_query.filters) == 2
assert item_query.filters[0].name == "sort[accuracy]"
assert item_query.filters[0].param == "desc"
12 changes: 12 additions & 0 deletions darwin/future/tests/meta/queries/test_item_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,15 @@ def test_get_specific_index_collects_correct_page(
json={"item_ids": [str(uuid) for uuid in str_ids[5:]]},
)
base_ItemIDQuery[7]


def test_sort_method(base_client: ClientCore):
item_id_query = ItemIDQuery(
base_client, meta_params={"dataset_id": 0000, "team_slug": "test_team"}
)

item_id_query.sort(accuracy="desc", byte_size="asc")

assert len(item_id_query.filters) == 2
assert item_id_query.filters[0].name == "sort[accuracy]"
assert item_id_query.filters[0].param == "desc"
28 changes: 16 additions & 12 deletions tests/darwin/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,31 +471,33 @@ def test_get_team_properties(self, darwin_client: Client) -> None:
@pytest.mark.usefixtures("file_read_write_test")
class TestCreateProperty:
@responses.activate
def test_create_property(self, darwin_client: Client, base_property_object: FullProperty) -> None:
def test_create_property(
self, darwin_client: Client, base_property_object: FullProperty
) -> None:
responses.add(
responses.POST,
"http://localhost/apiv2/teams/v7-darwin-json-v1/properties",
json=base_property_object.dict(),
status=200,
)
_property = darwin_client.create_property(
team_slug="v7-darwin-json-v1",
params=base_property_object
team_slug="v7-darwin-json-v1", params=base_property_object
)
assert isinstance(_property, FullProperty)
assert _property == base_property_object

@responses.activate
def test_create_property_from_json(self, darwin_client: Client, base_property_object: FullProperty) -> None:
def test_create_property_from_json(
self, darwin_client: Client, base_property_object: FullProperty
) -> None:
responses.add(
responses.POST,
"http://localhost/apiv2/teams/v7-darwin-json-v1/properties",
json=base_property_object.dict(),
status=200,
)
_property = darwin_client.create_property(
team_slug="v7-darwin-json-v1",
params=base_property_object.dict()
team_slug="v7-darwin-json-v1", params=base_property_object.dict()
)
assert isinstance(_property, FullProperty)
assert _property == base_property_object
Expand All @@ -504,7 +506,9 @@ def test_create_property_from_json(self, darwin_client: Client, base_property_ob
@pytest.mark.usefixtures("file_read_write_test")
class TestUpdateProperty:
@responses.activate
def test_update_property(self, darwin_client: Client, base_property_object: FullProperty) -> None:
def test_update_property(
self, darwin_client: Client, base_property_object: FullProperty
) -> None:
property_id = base_property_object.id
responses.add(
responses.PUT,
Expand All @@ -513,14 +517,15 @@ def test_update_property(self, darwin_client: Client, base_property_object: Full
status=200,
)
_property = darwin_client.update_property(
team_slug="v7-darwin-json-v1",
params=base_property_object
team_slug="v7-darwin-json-v1", params=base_property_object
)
assert isinstance(_property, FullProperty)
assert _property == base_property_object

@responses.activate
def test_update_property_from_json(self, darwin_client: Client, base_property_object: FullProperty) -> None:
def test_update_property_from_json(
self, darwin_client: Client, base_property_object: FullProperty
) -> None:
property_id = base_property_object.id
responses.add(
responses.PUT,
Expand All @@ -529,8 +534,7 @@ def test_update_property_from_json(self, darwin_client: Client, base_property_ob
status=200,
)
_property = darwin_client.update_property(
team_slug="v7-darwin-json-v1",
params=base_property_object.dict()
team_slug="v7-darwin-json-v1", params=base_property_object.dict()
)
assert isinstance(_property, FullProperty)
assert _property == base_property_object
60 changes: 46 additions & 14 deletions tests/darwin/exporter/formats/export_darwin_1_0_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
class TestBuildJson:
def test_empty_annotation_file(self):
annotation_file = dt.AnnotationFile(
path=Path("test.json"), filename="test.json", annotation_classes=[], annotations=[]
path=Path("test.json"),
filename="test.json",
annotation_classes=[],
annotations=[],
)

assert _build_json(annotation_file) == {
Expand Down Expand Up @@ -119,8 +122,12 @@ def test_complex_polygon(self):
],
]

annotation_class = dt.AnnotationClass(name="test", annotation_type="complex_polygon")
annotation = dt.Annotation(annotation_class=annotation_class, data={"paths": polygon_path}, subs=[])
annotation_class = dt.AnnotationClass(
name="test", annotation_type="complex_polygon"
)
annotation = dt.Annotation(
annotation_class=annotation_class, data={"paths": polygon_path}, subs=[]
)

annotation_file = dt.AnnotationFile(
path=Path("test.json"),
Expand Down Expand Up @@ -225,7 +232,9 @@ def test_complex_polygon_with_bbox(self):

bounding_box = {"x": 557.66, "y": 428.98, "w": 160.76, "h": 315.3}

annotation_class = dt.AnnotationClass(name="test", annotation_type="complex_polygon")
annotation_class = dt.AnnotationClass(
name="test", annotation_type="complex_polygon"
)
annotation = dt.Annotation(
annotation_class=annotation_class,
data={"paths": polygon_path, "bounding_box": bounding_box},
Expand Down Expand Up @@ -267,8 +276,12 @@ def test_complex_polygon_with_bbox(self):

def test_bounding_box(self):
bounding_box_data = {"x": 100, "y": 150, "w": 50, "h": 30}
annotation_class = dt.AnnotationClass(name="bbox_test", annotation_type="bounding_box")
annotation = dt.Annotation(annotation_class=annotation_class, data=bounding_box_data, subs=[])
annotation_class = dt.AnnotationClass(
name="bbox_test", annotation_type="bounding_box"
)
annotation = dt.Annotation(
annotation_class=annotation_class, data=bounding_box_data, subs=[]
)

annotation_file = dt.AnnotationFile(
path=Path("test.json"),
Expand Down Expand Up @@ -305,7 +318,9 @@ def test_bounding_box(self):
def test_tags(self):
tag_data = "sample_tag"
annotation_class = dt.AnnotationClass(name="tag_test", annotation_type="tag")
annotation = dt.Annotation(annotation_class=annotation_class, data=tag_data, subs=[])
annotation = dt.Annotation(
annotation_class=annotation_class, data=tag_data, subs=[]
)

annotation_file = dt.AnnotationFile(
path=Path("test.json"),
Expand Down Expand Up @@ -343,7 +358,9 @@ def test_polygon_annotation_file_with_bbox(self):

annotation_class = dt.AnnotationClass(name="test", annotation_type="polygon")
annotation = dt.Annotation(
annotation_class=annotation_class, data={"path": polygon_path, "bounding_box": bounding_box}, subs=[]
annotation_class=annotation_class,
data={"path": polygon_path, "bounding_box": bounding_box},
subs=[],
)

annotation_file = dt.AnnotationFile(
Expand All @@ -369,7 +386,12 @@ def test_polygon_annotation_file_with_bbox(self):
"workview_url": None,
},
"annotations": [
{"polygon": {"path": polygon_path}, "name": "test", "slot_names": [], "bounding_box": bounding_box}
{
"polygon": {"path": polygon_path},
"name": "test",
"slot_names": [],
"bounding_box": bounding_box,
}
],
"dataset": "None",
}
Expand All @@ -396,9 +418,13 @@ def test_complex_polygon_with_bbox(self):

bounding_box = {"x": 557.66, "y": 428.98, "w": 160.76, "h": 315.3}

annotation_class = dt.AnnotationClass(name="test", annotation_type="complex_polygon")
annotation_class = dt.AnnotationClass(
name="test", annotation_type="complex_polygon"
)
annotation = dt.Annotation(
annotation_class=annotation_class, data={"paths": polygon_path, "bounding_box": bounding_box}, subs=[]
annotation_class=annotation_class,
data={"paths": polygon_path, "bounding_box": bounding_box},
subs=[],
)

annotation_file = dt.AnnotationFile(
Expand Down Expand Up @@ -436,8 +462,12 @@ def test_complex_polygon_with_bbox(self):

def test_bounding_box(self):
bounding_box_data = {"x": 100, "y": 150, "w": 50, "h": 30}
annotation_class = dt.AnnotationClass(name="bbox_test", annotation_type="bounding_box")
annotation = dt.Annotation(annotation_class=annotation_class, data=bounding_box_data, subs=[])
annotation_class = dt.AnnotationClass(
name="bbox_test", annotation_type="bounding_box"
)
annotation = dt.Annotation(
annotation_class=annotation_class, data=bounding_box_data, subs=[]
)

annotation_file = dt.AnnotationFile(
path=Path("test.json"),
Expand Down Expand Up @@ -474,7 +504,9 @@ def test_bounding_box(self):
def test_tags(self):
tag_data = "sample_tag"
annotation_class = dt.AnnotationClass(name="tag_test", annotation_type="tag")
annotation = dt.Annotation(annotation_class=annotation_class, data=tag_data, subs=[])
annotation = dt.Annotation(
annotation_class=annotation_class, data=tag_data, subs=[]
)

annotation_file = dt.AnnotationFile(
path=Path("test.json"),
Expand Down

0 comments on commit 8028311

Please sign in to comment.