Skip to content

Commit

Permalink
Updated Task model to include status badges
Browse files Browse the repository at this point in the history
  • Loading branch information
mbsantiago committed Sep 28, 2023
1 parent bdf9280 commit cc7e79a
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 32 deletions.
6 changes: 3 additions & 3 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion src/soundevent/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
"""

from soundevent.data.annotation_projects import AnnotationProject
from soundevent.data.annotation_tasks import AnnotationTask
from soundevent.data.annotation_tasks import (
AnnotationTask,
StatusBadge,
TaskState,
)
from soundevent.data.annotations import Annotation
from soundevent.data.clips import Clip
from soundevent.data.dataset import Dataset
Expand Down Expand Up @@ -90,7 +94,9 @@
"Recording",
"Sequence",
"SoundEvent",
"StatusBadge",
"Tag",
"TaskState",
"Time",
"TimeInterval",
"TimeStamp",
Expand Down
42 changes: 34 additions & 8 deletions src/soundevent/data/annotation_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"""
import datetime
from enum import Enum
from typing import List, Optional
from uuid import UUID, uuid4

Expand All @@ -59,6 +60,37 @@
from soundevent.data.tags import Tag


class TaskState(Enum):
"""Task state."""

assigned = "assigned"
"""Task has been assigned to an annotator."""

completed = "completed"
"""Task has been completed by an annotator."""

verified = "verified"
"""Task has been verified by a reviewer."""

rejected = "rejected"
"""Task has been rejected by a reviewer."""


class StatusBadge(BaseModel):
"""Annotation Status Badge."""

state: TaskState
"""State of the annotation task."""

user: Optional[str] = None
"""User who is responsible for this status badge."""

created_at: datetime.datetime = Field(
default_factory=datetime.datetime.now
)
"""Date and time when the status badge was created."""


class AnnotationTask(BaseModel):
"""Annotation task."""

Expand All @@ -71,20 +103,14 @@ class AnnotationTask(BaseModel):
annotations: List[Annotation] = Field(default_factory=list)
"""List of annotations in the created during the annotation task."""

completed_by: Optional[str] = None
"""The user who completed the annotation task."""

completed_on: Optional[datetime.datetime] = None
"""The date and time when the annotation task was completed."""

notes: List[Note] = Field(default_factory=list)
"""Notes associated with the annotation task."""

tags: List[Tag] = Field(default_factory=list)
"""User provided tags to the annotated clip."""

completed: bool = False
"""Whether the annotation task has been completed."""
status_badges: List[StatusBadge] = Field(default_factory=list)
"""Status badges for the annotation task."""

def __hash__(self):
"""Compute the hash value for the annotation task."""
Expand Down
4 changes: 2 additions & 2 deletions src/soundevent/data/geometries.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ class BaseGeometry(BaseModel, ABC):
type: GeometryType = Field(
description="the type of geometry used to locate the sound event.",
frozen=True,
include=True,
exclude=False,
)

coordinates: Union[float, List] = Field(
description="the coordinates of the geometry.",
frozen=True,
include=True,
exclude=False,
)

_geom: ShapelyBaseGeometry = PrivateAttr()
Expand Down
14 changes: 3 additions & 11 deletions src/soundevent/io/formats/aoef.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,11 +519,7 @@ class AnnotationTaskObject(BaseModel):

annotations: Optional[List[int]] = None

completed_by: Optional[str] = None

completed_on: Optional[datetime.datetime] = None

completed: bool
status_badges: Optional[List[data.StatusBadge]] = None

notes: Optional[List[data.Note]] = None

Expand Down Expand Up @@ -570,11 +566,9 @@ def from_annotation_task(
audio_dir=audio_dir,
).id,
annotations=annotation_ids if annotation_ids else None,
completed_by=task.completed_by,
completed_on=task.completed_on,
completed=task.completed,
notes=task.notes if task.notes else None,
tags=tag_ids if tag_ids else None,
status_badges=task.status_badges if task.status_badges else None,
)

return tasks[task]
Expand All @@ -595,9 +589,7 @@ def to_annotation_task(
annotations[annotation_id]
for annotation_id in (self.annotations or [])
],
completed_by=self.completed_by,
completed_on=self.completed_on,
completed=self.completed,
status_badges=self.status_badges if self.status_badges else [],
notes=self.notes if self.notes else [],
tags=[tags[tag_id] for tag_id in (self.tags or [])],
)
Expand Down
41 changes: 34 additions & 7 deletions tests/test_io/test_annotation_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,16 @@ def test_can_recover_task_status(
# Arrange
annotation_project = data.AnnotationProject(
name="test_project",
tasks=[data.AnnotationTask(clip=clip, completed=True)],
tasks=[
data.AnnotationTask(
clip=clip,
status_badges=[
data.StatusBadge(
state=data.TaskState.completed,
)
],
)
],
)
path = tmp_path / "test_project.json"

Expand All @@ -183,7 +192,9 @@ def test_can_recover_task_status(

# Assert
assert recovered == annotation_project
assert recovered.tasks[0].completed
assert (
recovered.tasks[0].status_badges[0].state == data.TaskState.completed
)


def test_can_recover_user_that_completed_task(tmp_path: Path, clip: data.Clip):
Expand All @@ -194,8 +205,12 @@ def test_can_recover_user_that_completed_task(tmp_path: Path, clip: data.Clip):
tasks=[
data.AnnotationTask(
clip=clip,
completed=True,
completed_by="test_user",
status_badges=[
data.StatusBadge(
state=data.TaskState.completed,
user="test_user",
)
],
)
],
)
Expand All @@ -207,7 +222,9 @@ def test_can_recover_user_that_completed_task(tmp_path: Path, clip: data.Clip):

# Assert
assert recovered == annotation_project
assert recovered.tasks[0].completed_by == "test_user"
badge = recovered.tasks[0].status_badges[0]
assert badge.state == data.TaskState.completed
assert badge.user == "test_user"


def test_can_recover_task_notes(tmp_path: Path, clip: data.Clip):
Expand Down Expand Up @@ -241,7 +258,15 @@ def test_can_recover_task_completion_date(tmp_path: Path, clip: data.Clip):
annotation_project = data.AnnotationProject(
name="test_project",
tasks=[
data.AnnotationTask(clip=clip, completed=True, completed_on=date)
data.AnnotationTask(
clip=clip,
status_badges=[
data.StatusBadge(
state=data.TaskState.completed,
created_at=date,
)
],
)
],
)
path = tmp_path / "test_project.json"
Expand All @@ -252,7 +277,9 @@ def test_can_recover_task_completion_date(tmp_path: Path, clip: data.Clip):

# Assert
assert recovered == annotation_project
assert recovered.tasks[0].completed_on == date
badge = recovered.tasks[0].status_badges[0]
assert badge.state == data.TaskState.completed
assert badge.created_at == date


def test_can_recover_task_simple_annotation(
Expand Down

0 comments on commit cc7e79a

Please sign in to comment.