|
5 | 5 | from typing import Any, List, Literal, Optional, Union |
6 | 6 |
|
7 | 7 | from aind_data_schema_models.modalities import Modality |
8 | | -from pydantic import Field, SkipValidation, model_validator |
| 8 | +from pydantic import Field, SkipValidation, field_validator, model_validator |
9 | 9 |
|
10 | 10 | from aind_data_schema.base import AwareDatetimeWithDefault, DataCoreModel, DataModel, DiscriminatedList |
11 | 11 | from aind_data_schema.utils.merge import merge_notes, merge_optional_list, remove_duplicates |
@@ -49,8 +49,9 @@ class QCMetric(DataModel): |
49 | 49 | status_history: List[QCStatus] = Field(default=[], title="Metric status history", min_length=1) |
50 | 50 | description: Optional[str] = Field(default=None, title="Metric description") |
51 | 51 | reference: Optional[str] = Field(default=None, title="Metric reference image URL or plot type") |
52 | | - tags: List[str] = Field( |
53 | | - default=[], title="Tags", description="Tags group QCMetric objects to allow for grouping and filtering" |
| 52 | + tags: dict[str, str] = Field( |
| 53 | + default={}, title="Tags", |
| 54 | + description="Tags group QCMetric objects. Unique keys define groups of tags, for example {'probe': 'probeA'}." |
54 | 55 | ) |
55 | 56 | evaluated_assets: Optional[List[str]] = Field( |
56 | 57 | default=None, |
@@ -80,6 +81,28 @@ def validate_multi_asset(self): |
80 | 81 | raise ValueError(f"Metric '{self.name}' is a single-asset metric and should not have evaluated_assets") |
81 | 82 | return self |
82 | 83 |
|
| 84 | + @model_validator(mode="before") |
| 85 | + @classmethod |
| 86 | + def fix_tag_lists(cls, self): |
| 87 | + """Convert tags from list to dict if necessary |
| 88 | +
|
| 89 | + This function is for backwards compatibility with v2.2.X where tags were stored as lists of strings. |
| 90 | +
|
| 91 | + Remove this function in aind-data-schema v3.X |
| 92 | + """ |
| 93 | + tags = self["tags"] |
| 94 | + if isinstance(tags, list): |
| 95 | + # Convert list of strings to dict with string keys |
| 96 | + if len(tags) == 1: |
| 97 | + self["tags"] = { |
| 98 | + "tag": tags[0], |
| 99 | + "name": self["name"], |
| 100 | + } |
| 101 | + else: |
| 102 | + # Unfortunately there is no reasonable way to handle multiple tags, these assets should be re-generated |
| 103 | + self["tags"] = {f"tag_{i+1}": tag for i, tag in enumerate(tags)} |
| 104 | + return self |
| 105 | + |
83 | 106 |
|
84 | 107 | class CurationHistory(DataModel): |
85 | 108 | """Schema to track curator name and timestamp for curation events""" |
@@ -110,15 +133,15 @@ class QualityControl(DataCoreModel): |
110 | 133 | ) |
111 | 134 | notes: Optional[str] = Field(default=None, title="Notes") |
112 | 135 |
|
113 | | - default_grouping: List[str] = Field( |
| 136 | + default_grouping: List[list[str]] = Field( |
114 | 137 | ..., |
115 | 138 | title="Default grouping", |
116 | | - description="Default tag grouping for this QualityControl object, used in visualizations", |
| 139 | + description="Tag *keys* that should be used to group metrics hierarchically for visualization", |
117 | 140 | ) |
118 | | - allow_tag_failures: List[str | tuple] = Field( |
| 141 | + allow_tag_failures: List[str] = Field( |
119 | 142 | default=[], |
120 | 143 | title="Allow tag failures", |
121 | | - description="List of tags that are allowed to fail without failing the overall QC", |
| 144 | + description="List of tag *values* that are allowed to fail without failing the overall QC", |
122 | 145 | ) |
123 | 146 | status: Optional[dict] = Field( |
124 | 147 | default=None, |
@@ -257,6 +280,17 @@ def __add__(self, other: "QualityControl") -> "QualityControl": |
257 | 280 | allow_tag_failures=combined_allow_tag_failures, |
258 | 281 | ) |
259 | 282 |
|
| 283 | + @field_validator("default_grouping", mode="before") |
| 284 | + def fix_default_grouping_list(cls, value: dict) -> dict: |
| 285 | + """Convert default grouping from list of strings to list of list of strings if necessary |
| 286 | + This function is for backwards compatibility with v2.2.X where default_grouping was stored as a list of strings. |
| 287 | + Remove this function in aind-data-schema v3.X |
| 288 | + """ |
| 289 | + if value and len(value) > 0 and isinstance(value[0], str): |
| 290 | + # Convert list of strings to list of list of strings |
| 291 | + value = [[tag] for tag in value] |
| 292 | + return value |
| 293 | + |
260 | 294 |
|
261 | 295 | def _get_status_by_date(metric: QCMetric | CurationMetric, date: datetime) -> Status: |
262 | 296 | """Get the status of a metric at a specific date by looking through status_history. |
|
0 commit comments