Skip to content

Commit

Permalink
Merge branch 'master' into py-647
Browse files Browse the repository at this point in the history
  • Loading branch information
Nathanjp91 authored Dec 19, 2023
2 parents e1f7bfb + 79859ab commit 235d905
Show file tree
Hide file tree
Showing 31 changed files with 1,225 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"editor.formatOnSave": true,
"editor.tabSize": 4,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "ms-python.black-formatter"
},
Expand Down
48 changes: 48 additions & 0 deletions darwin/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
Unauthorized,
ValidationError,
)
from darwin.future.core.client import ClientCore, DarwinConfig
from darwin.future.core.properties import create_property as create_property_future
from darwin.future.core.properties import (
get_team_full_properties as get_team_full_properties_future,
)
from darwin.future.core.properties import (
get_team_properties as get_team_properties_future,
)
from darwin.future.core.properties import update_property as update_property_future
from darwin.future.core.types.common import JSONDict
from darwin.future.data_objects.properties import FullProperty
from darwin.item import DatasetItem
from darwin.utils import (
get_response_content,
Expand Down Expand Up @@ -1474,3 +1485,40 @@ def api_v2(self) -> BackendV2:
if not team:
raise ValueError("No team was found.")
return BackendV2(self, team.slug)

def get_team_properties(
self, team_slug: Optional[str] = None, include_property_values: bool = True
) -> List[FullProperty]:
darwin_config = DarwinConfig.from_old(self.config)
future_client = ClientCore(darwin_config)

if not include_property_values:
return get_team_properties_future(
client=future_client,
team_slug=team_slug or self.default_team,
)

return get_team_full_properties_future(
client=future_client,
team_slug=team_slug or self.default_team,
)

def create_property(self, team_slug: Optional[str], params: Union[FullProperty, JSONDict]) -> FullProperty:
darwin_config = DarwinConfig.from_old(self.config)
future_client = ClientCore(darwin_config)

return create_property_future(
client=future_client,
team_slug=team_slug or self.default_team,
params=params,
)

def update_property(self, team_slug: Optional[str], params: Union[FullProperty, JSONDict]) -> FullProperty:
darwin_config = DarwinConfig.from_old(self.config)
future_client = ClientCore(darwin_config)

return update_property_future(
client=future_client,
team_slug=team_slug or self.default_team,
params=params,
)
1 change: 0 additions & 1 deletion darwin/dataset/local_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,6 @@ def get_annotation_filepaths(

if partition is None:
return (str(e) for e in sorted(annotations_dir.glob("**/*.json")))

if split_type == "random":
split_filename = f"{split_type}_{partition}.txt"
elif split_type == "stratified":
Expand Down
9 changes: 9 additions & 0 deletions darwin/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
except ImportError:
NDArray = Any # type:ignore

from darwin.future.data_objects.properties import SelectedProperty
from darwin.path_utils import construct_full_path, is_properties_enabled, parse_metadata

# Utility types
Expand Down Expand Up @@ -215,6 +216,9 @@ class Annotation:
# The darwin ID of this annotation.
id: Optional[str] = None

# Properties of this annotation.
properties: Optional[list[SelectedProperty]] = None

def get_sub(self, annotation_type: str) -> Optional[SubAnnotation]:
"""
Returns the first SubAnnotation that matches the given type.
Expand Down Expand Up @@ -269,6 +273,9 @@ class VideoAnnotation:
# The darwin ID of this annotation.
id: Optional[str] = None

# Properties of this annotation.
properties: Optional[list[SelectedProperty]] = None

def get_data(
self,
only_keyframes: bool = True,
Expand Down Expand Up @@ -1272,6 +1279,7 @@ def make_video_annotation(
segments: List[Segment],
interpolated: bool,
slot_names: List[str],
properties: Optional[list[SelectedProperty]] = None,
) -> VideoAnnotation:
"""
Creates and returns a ``VideoAnnotation``.
Expand Down Expand Up @@ -1308,6 +1316,7 @@ def make_video_annotation(
segments,
interpolated,
slot_names=slot_names or [],
properties=properties,
)


Expand Down
49 changes: 14 additions & 35 deletions darwin/exporter/formats/nifti.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ast
import json as native_json
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Tuple, Union
Expand Down Expand Up @@ -137,50 +138,28 @@ def check_for_error_and_return_imageid(
image_id : str
"""
# check if all item slots have the correct file-extension
# Check if all item slots have the correct file-extension
for slot in video_annotation.slots:
for source_file in slot.source_files:
filename = Path(source_file["file_name"])

try:
suffixes = filename.suffixes[-2:]
except IndexError:
suffixes = filename.suffixes
if len(suffixes) == 2:
if suffixes[0] == ".nii" and suffixes[1] == ".gz":
image_id = str(filename).rstrip("".join(suffixes))
else:
return create_error_message_json(
"Two suffixes found but not ending in .nii.gz",
output_dir,
str(filename),
)
elif len(suffixes) == 1:
if suffixes[0] == ".nii" or suffixes[0] == ".dcm":
image_id = filename.stem
else:
return create_error_message_json(
"Misconfigured filename, not ending in .nii or .dcm. Are you sure this is medical data?",
output_dir,
str(filename),
)
else:
if not (
filename.name.lower().endswith(".nii.gz")
or filename.name.lower().endswith(".nii")
or filename.name.lower().endswith(".dcm")
):
return create_error_message_json(
"You are trying to export to nifti. Filename should contain either .nii, .nii.gz or .dcm extension."
"Are you sure this is medical data?",
"Misconfigured filename, not ending in .nii, .nii.gz or .dcm. Are you sure this is medical data?",
output_dir,
str(filename),
)

filename = Path(video_annotation.filename)
try:
suffixes = filename.suffixes[-2:]
except IndexError:
suffixes = filename.suffixes
if len(suffixes) == 2:
image_id = str(filename).rstrip("".join(suffixes))
elif len(suffixes) == 1:
image_id = str(filename.stem)
if filename.name.lower().endswith(".nii.gz"):
image_id = re.sub(r"(?i)\.nii\.gz$", "", str(filename))
elif filename.name.lower().endswith(".nii"):
image_id = re.sub(r"(?i)\.nii$", "", str(filename))
elif filename.name.lower().endswith(".dcm"):
image_id = re.sub(r"(?i)\.dcm$", "", str(filename))
else:
image_id = str(filename)

Expand Down
20 changes: 20 additions & 0 deletions darwin/future/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pydantic import BaseModel, root_validator, validator
from requests.adapters import HTTPAdapter, Retry

from darwin.config import Config as OldConfig
from darwin.future.core.types.common import JSONType, QueryString
from darwin.future.exceptions import (
BadRequest,
Expand Down Expand Up @@ -121,6 +122,25 @@ def from_api_key_with_defaults(api_key: str) -> DarwinConfig:
datasets_dir=DarwinConfig._default_config_path(),
)

@staticmethod
def from_old(old_config: OldConfig) -> DarwinConfig:
teams = old_config.get("teams")
if not teams:
raise ValueError("No teams found in the old config")

default_team = old_config.get("global/default_team")
if not default_team:
default_team = list(teams.keys())[0]

return DarwinConfig(
api_key=teams[default_team]["api_key"],
api_endpoint=old_config.get("global/api_endpoint"),
base_url=old_config.get("global/base_url"),
default_team=default_team,
teams=teams,
datasets_dir=teams[default_team]["datasets_dir"],
)

class Config:
validate_assignment = True

Expand Down
2 changes: 2 additions & 0 deletions darwin/future/core/properties/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from darwin.future.core.properties.create import create_property
from darwin.future.core.properties.get import (
get_property_by_id,
get_team_full_properties,
get_team_properties,
)
from darwin.future.core.properties.update import update_property, update_property_value
36 changes: 36 additions & 0 deletions darwin/future/core/properties/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Optional, Union

from pydantic import parse_obj_as

from darwin.future.core.client import ClientCore
from darwin.future.core.types.common import JSONDict
from darwin.future.data_objects.properties import FullProperty


def create_property(
client: ClientCore,
params: Union[FullProperty, JSONDict],
team_slug: Optional[str] = None,
) -> FullProperty:
"""
Creates a property for the specified team slug.
Parameters:
client (ClientCore): The client to use for the request.
team_slug (Optional[str]): The slug of the team to get. If not specified, the
default team from the client's config will be used.
params (Optional[JSONType]): The JSON data to use for the request.
Returns:
FullProperty: FullProperty object for the created property.
Raises:
HTTPError: If the response status code is not in the 200-299 range.
"""
if not team_slug:
team_slug = client.config.default_team
if isinstance(params, FullProperty):
params = params.to_create_endpoint()
response = client.post(f"/v2/teams/{team_slug}/properties", data=params)
assert isinstance(response, dict)
return parse_obj_as(FullProperty, response)
2 changes: 1 addition & 1 deletion darwin/future/core/properties/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ def get_property_by_id(
"""
if not team_slug:
team_slug = client.config.default_team
response = client.get(f"/v2/teams/{team_slug}/properties/{property_id}")
response = client.get(f"/v2/teams/{team_slug}/properties/{str(property_id)}")
assert isinstance(response, dict)
return parse_obj_as(FullProperty, response)
74 changes: 74 additions & 0 deletions darwin/future/core/properties/update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from typing import Optional, Union

from pydantic import parse_obj_as

from darwin.future.core.client import ClientCore
from darwin.future.core.types.common import JSONDict
from darwin.future.data_objects.properties import FullProperty, PropertyValue


def update_property(
client: ClientCore,
params: Union[FullProperty, JSONDict],
team_slug: Optional[str] = None,
) -> FullProperty:
"""
Updates a property for the specified team slug.
Parameters:
client (ClientCore): The client to use for the request.
team_slug (Optional[str]): The slug of the team to get. If not specified, the
default team from the client's config will be used.
params (Optional[JSONType]): The JSON data to use for the request.
Returns:
FullProperty: FullProperty object for the created property.
Raises:
HTTPError: If the response status code is not in the 200-299 range.
"""
if not team_slug:
team_slug = client.config.default_team
if isinstance(params, FullProperty):
id, params = params.to_update_endpoint()
else:
id = params.get("id")
del params["id"]
response = client.put(f"/v2/teams/{team_slug}/properties/{id}", data=params)
assert isinstance(response, dict)
return parse_obj_as(FullProperty, response)


def update_property_value(
client: ClientCore,
params: Union[PropertyValue, JSONDict],
item_id: str,
team_slug: Optional[str] = None,
) -> PropertyValue:
"""
Updates a property value for the specified property id.
Parameters:
client (ClientCore): The client to use for the request.
team_slug (Optional[str]): The slug of the team to get. If not specified, the
default team from the client's config will be used.
params (Optional[JSONType]): The JSON data to use for the request.
Returns:
FullProperty: FullProperty object for the created property.
Raises:
HTTPError: If the response status code is not in the 200-299 range.
"""
if not team_slug:
team_slug = client.config.default_team
if isinstance(params, PropertyValue):
id, params = params.to_update_endpoint()
else:
id = params.get("id")
del params["id"]
response = client.put(
f"/v2/teams/{team_slug}/properties/{item_id}/property_values/{id}", data=params
)
assert isinstance(response, dict)
return parse_obj_as(PropertyValue, response)
1 change: 1 addition & 0 deletions darwin/future/core/types/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from darwin.future.data_objects import validators as darwin_validators

JSONType = Union[Dict[str, Any], List[Dict[str, Any]]] # type: ignore
JSONDict = Dict[str, Any] # type: ignore


class Implements_str(Protocol):
Expand Down
Loading

0 comments on commit 235d905

Please sign in to comment.