#!/usr/bin/env python3
# =========================================================================
# Program: iota2
#
# Copyright (c) CESBIO. All rights reserved.
#
# See LICENSE for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the above copyright notices for more information.
#
# =========================================================================
"""Utility classes for labels"""
import re
from typing import ClassVar
import pandas as pd
from pydantic import BaseModel, validator
[docs]class I2Label(BaseModel):
"""Utility class to define labels."""
separator: ClassVar[str] = "_"
sensor_name: str
feat_name: str
@validator("sensor_name", "feat_name")
@classmethod
def val_sensor_name(cls, value: str) -> str:
"""Validate sensor_name and feat_name attributes, values are lowered."""
pattern = r"[a-zA-z]*[0-9]*"
matches = re.findall(pattern, value)[0:-1]
if "" in matches:
raise ValueError(
f"sensor_name and feat_name must match regular expression '{pattern}'"
)
if cls.separator in value:
raise ValueError(
f"'{cls.separator}' forbidden in sensor_name and feat_name "
)
return value.lower()
def __str__(self) -> str:
return f"{self.sensor_name}{self.separator}{self.feat_name}"
[docs]class I2TemporalLabel(I2Label):
"""Utility class to define temporal labels."""
date: str
@validator("date")
@classmethod
def val_date(cls, value: str) -> str:
"""Validate date attribute, returned as YYYYMMDD."""
# let pandas guess the incoming date format
value_pd = pd.to_datetime(value, infer_datetime_format=True)
out: str = value_pd.strftime("%Y%m%d")
return out
def __str__(self) -> str:
return f"{self.sensor_name}{self.separator}{self.feat_name}{self.separator}{self.date}"
I2LabelAlias = I2Label | I2TemporalLabel
[docs]def i2_label_factory(label: str) -> I2LabelAlias:
"""Generate labels object."""
sep = I2Label.separator
if len(label.split(sep)) == 2:
out_label = I2Label.parse_obj(dict(zip(I2Label.__fields__, label.split(sep))))
elif len(label.split(sep)) == 3:
out_label = I2TemporalLabel.parse_obj(
dict(zip(I2TemporalLabel.__fields__, label.split(sep)))
)
else:
expected_templab = I2TemporalLabel.construct(
sensor_name="sensorname", feat_name="featurename", date="date"
)
expected_lab = I2Label.construct(
sensor_name="sensorname", feat_name="featurename"
)
raise ValueError(
"Label is not formatted correctly. "
f"Format labels as {expected_templab} "
f"or {expected_lab}"
)
return out_label # type: ignore
# mypy infers out_label as Any due to pydantic typing from parse_obj() method