Skip to content

Commit

Permalink
Merge branch 'release-202311-Eval2-v1.0.1'
Browse files Browse the repository at this point in the history
* release-202311-Eval2-v1.0.1:
  Renamed eval logger to be specific to MIT-LL
  Added some TODO's to use config structures instead of raw loads
  Use common constant vars instead of floating strings
  Spread the config love
  Add Eval2 logging for emitted error notifications
  Style fixups
  Add logging node for Eval2 requested format
  Added node to translate TaskUpdate messages to broad level
  Add activity label config structs
  Add common task config structures and load functions
  Add another time conversion function
  Hannah requested enabling of all task configs in multi config
  Update log message in GSP node to indicate which task it pertained
  • Loading branch information
Purg committed Nov 17, 2023
2 parents 10d0f5d + e823f4e commit 4584271
Show file tree
Hide file tree
Showing 12 changed files with 726 additions and 58 deletions.
162 changes: 160 additions & 2 deletions angel_system/data/config_structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
Structures related to configuration files.
"""

from dataclasses import dataclass, field
from dataclasses import dataclass
from os import PathLike
from pathlib import Path
from typing import cast
from typing import Dict
from typing import Optional
from typing import Sequence
from typing import Tuple

Expand Down Expand Up @@ -34,7 +36,7 @@ class ObjectLabelSet:

def __post_init__(self):
# coerce nested label objects into the ObjectLabel type.
if self.labels and not isinstance(self.labels, ObjectLabel):
if self.labels and not isinstance(self.labels[0], ObjectLabel):
raw_labels = cast(Sequence[Dict], self.labels)
self.labels = tuple(ObjectLabel(**rl) for rl in raw_labels)

Expand All @@ -50,3 +52,159 @@ def load_object_label_set(filepath: PathLike) -> ObjectLabelSet:
with open(filepath) as infile:
data = yaml.safe_load(infile)
return ObjectLabelSet(**data)


@dataclass
class ActivityLabel:
"""
One activity classification ID and paired label information.
"""

# Identifier integer for this activity label
id: int
# Concise string label for this activity. Should not contain any spaces.
label: str
# Full sentence description of this activity.
full_str: str
# Optional integer representing how many times an activity should be
# repeated to be considered "full"
# TODO: This parameter has ambiguous and violated meaning (not used as
# intended if at all).
repeat: Optional[int] = None


@dataclass
class ActivityLabelSet:
version: str
title: str
labels: Tuple[ActivityLabel]

def __post_init__(self):
# coerce nested label objects into the ObjectLabel type.
if self.labels and not isinstance(self.labels[0], ActivityLabel):
raw_labels = cast(Sequence[Dict], self.labels)
self.labels = tuple(ActivityLabel(**rl) for rl in raw_labels)


def load_activity_label_set(filepath: PathLike) -> ActivityLabelSet:
"""
Load from YAML file an activity label set configuration.
:param filepath: Filepath to load from.
:return: Structure containing the loaded configuration.
"""
with open(filepath) as infile:
data = yaml.safe_load(infile)
return ActivityLabelSet(**data)


@dataclass
class TaskStep:
"""
A single task step with activity components.
"""

id: int
label: str
full_str: str
activity_ids: Tuple[int]


@dataclass
class LinearTask:
"""
A linear task with steps composed of activities.
"""

version: str
title: str
labels: Tuple[TaskStep]

def __post_init__(self):
# Coerce pathlike input (str) into a Path instance if not already.
if self.labels and not isinstance(self.labels[0], TaskStep):
raw = cast(Sequence[Dict], self.labels)
self.labels = tuple(TaskStep(**r) for r in raw)


def load_linear_task_config(filepath: PathLike) -> LinearTask:
"""
Load from YAML file a linear task configuration.
:param filepath: Filepath to load from.
:return: Structure containing the loaded configuration.
"""
with open(filepath) as infile:
data = yaml.safe_load(infile)
return LinearTask(**data)


@dataclass
class OneTaskConfig:
"""
Specification of where one task configuration is located.
"""

id: int
label: str
config_file: Path
active: bool

def __post_init__(self):
# Coerce pathlike input (str) into a Path instance if not already.
# Interpret relative paths now to absolute based on current working
# directory.
if not isinstance(self.config_file, Path):
self.config_file = Path(self.config_file).absolute()


@dataclass
class MultiTaskConfig:
"""
A collection of linear task configurations.
"""

version: str
title: str
tasks: Tuple[OneTaskConfig]

def __post_init__(self):
# coerce nested task objects into OneTaskConfig types
if self.tasks and not isinstance(self.tasks[0], OneTaskConfig):
raw = cast(Sequence[Dict], self.tasks)
self.tasks = tuple(OneTaskConfig(**r) for r in raw)


def load_multi_task_config(filepath: PathLike):
"""
Relative file paths are currently interpreted relative to the current
working directory and resolved to be absolute.
:param filepath: Filepath to load from.
:return: Structure containing the loaded configuration.
"""
with open(filepath) as infile:
data = yaml.safe_load(infile)
return MultiTaskConfig(**data)


def load_active_task_configs(cfg: MultiTaskConfig) -> Dict[str, LinearTask]:
"""
Load task configurations that are enabled in the multitask configuration.
:param cfg: Multitask configuration to base loading on.
:raises FileNotFoundError: Configured task configuration file did not refer
to an open-able file.
:return: Mapping of task label from the input configuration to the
LinearTask instance loaded.
"""
return {
ct.label: load_linear_task_config(ct.config_file)
for ct in cfg.tasks
if ct.active
}
10 changes: 10 additions & 0 deletions angel_system/global_step_prediction/global_step_predictor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def __init__(
GlobalStepPredctor: based on a TCN activity classifier's activity classification
outputs + a set of recipes, track what step a user is on for multiple recipes.
"""
# TODO: make use of angel_system.data.config_structs instead of
# manually loading and accessing by string keys.
with open(activity_config_fpath, "r") as stream:
self.activity_config = yaml.safe_load(stream)
num_activity_classes = len(self.activity_config["labels"])
Expand Down Expand Up @@ -68,6 +70,8 @@ def __init__(
self.activity_conf_history = np.empty((0, num_activity_classes))

self.recipe_types = recipe_types
# TODO: Expect use of angel_system.data.config_structs instead of
# a raw dictionary.
self.recipe_configs = recipe_config_dict

# Array of tracker dicts
Expand Down Expand Up @@ -116,6 +120,8 @@ def get_activity_order_from_config(self, config_fn):
Get the order of activity_ids (mapping to granular step
number) based on a recipe config
"""
# TODO: make use of angel_system.data.config_structs instead of
# manually loading and accessing by string keys.
with open(config_fn, "r") as stream:
config = yaml.safe_load(stream)
broad_steps = config["labels"]
Expand Down Expand Up @@ -208,6 +214,8 @@ def initialize_new_recipe_tracker(self, recipe, config_fn=None):
config_fn = self.recipe_configs[recipe]

# Read in task config
# TODO: make use of angel_system.data.config_structs instead of
# manually loading and accessing by string keys.
with open(config_fn, "r") as stream:
config = yaml.safe_load(stream)
labels = [self.sanitize_str(l["full_str"]) for l in config["labels"]]
Expand Down Expand Up @@ -1025,6 +1033,8 @@ def get_gt_steps_from_gt_activities(self, video_dset, config_fn):
def sanitize_str(str_: str):
return str_.lower().strip(" .")

# TODO: make use of angel_system.data.config_structs instead of
# manually loading and accessing by string keys.
with open(config_fn, "r") as stream:
config = yaml.safe_load(stream)
labels = [sanitize_str(l["label"]) for l in config["labels"]]
Expand Down
6 changes: 3 additions & 3 deletions config/tasks/multi-task-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ tasks:
- id: 1
label: "Tea"
config_file: "./config/tasks/recipe_tea.yaml"
active: false
active: true
- id: 2
label: "Pinwheel"
config_file: "./config/tasks/recipe_pinwheel.yaml"
active: false
active: true
- id: 3
label: "Oatmeal"
config_file: "./config/tasks/recipe_oatmeal.yaml"
active: false
active: true
- id: 4
label: "Dessert Quesadilla"
config_file: "./config/tasks/recipe_dessertquesadilla.yaml"
Expand Down
Empty file.
Loading

0 comments on commit 4584271

Please sign in to comment.