Add data_analysis/src/data_analysis/io_utils.py
This commit is contained in:
parent
6b0119f042
commit
90e5b03840
1 changed files with 104 additions and 0 deletions
104
data_analysis/src/data_analysis/io_utils.py
Normal file
104
data_analysis/src/data_analysis/io_utils.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
__all__ = ["LogEntry"]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LogEntryError(ValueError):
|
||||
"""Custom exception for LogEntry validation errors."""
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class LogEntry:
|
||||
"""Repräsentiert einen einzelnen Datensatz einer Rover-Messung.
|
||||
|
||||
Attribute:
|
||||
timestamp (datetime): Zeitstempel der Messung.
|
||||
luminosity (int): Lichtintensität in Lux.
|
||||
sound_level (float): Geräuschpegel in Dezibel A.
|
||||
temperature (float): Temperatur in Grad Celsius.
|
||||
inference (float): Inferenzergebnis (Wahrscheinlichkeit eines Ereignisses).
|
||||
"""
|
||||
|
||||
timestamp: datetime
|
||||
luminosity: int
|
||||
sound_level: float
|
||||
temperature: float
|
||||
inference: float
|
||||
raw: dict[str, Any] = field(default_factory=dict, repr=False)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
timestamp: str | datetime,
|
||||
luminosity: int | float,
|
||||
sound_level: float,
|
||||
temperature: float,
|
||||
inference: float,
|
||||
) -> None:
|
||||
try:
|
||||
if isinstance(timestamp, str):
|
||||
self.timestamp = datetime.fromisoformat(timestamp)
|
||||
elif isinstance(timestamp, datetime):
|
||||
self.timestamp = timestamp
|
||||
else:
|
||||
raise TypeError("timestamp must be str or datetime")
|
||||
|
||||
self.luminosity = int(luminosity)
|
||||
self.sound_level = float(sound_level)
|
||||
self.temperature = float(temperature)
|
||||
self.inference = float(inference)
|
||||
self.raw = {
|
||||
"t": self.timestamp.isoformat(),
|
||||
"Lx": self.luminosity,
|
||||
"dB": self.sound_level,
|
||||
"Temp": self.temperature,
|
||||
"Inference": self.inference,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error("Error initializing LogEntry: %s", e)
|
||||
raise LogEntryError(str(e)) from e
|
||||
|
||||
@classmethod
|
||||
def from_json_record(cls, record: dict[str, Any]) -> LogEntry:
|
||||
"""Erzeugt eine LogEntry-Instanz aus einem JSON-Datensatz und validiert Felder."""
|
||||
required = {"t", "Lx", "dB", "Temp", "Inference"}
|
||||
|
||||
missing = required - set(record.keys())
|
||||
if missing:
|
||||
raise LogEntryError(f"Missing fields: {', '.join(sorted(missing))}")
|
||||
|
||||
return cls(
|
||||
timestamp=record["t"],
|
||||
luminosity=record["Lx"],
|
||||
sound_level=record["dB"],
|
||||
temperature=record["Temp"],
|
||||
inference=record["Inference"],
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""Konvertiert den LogEntry zurück in ein Dictionary."""
|
||||
return self.raw.copy()
|
||||
|
||||
def __post_init__(self) -> None: # pragma: no cover
|
||||
# Redundant safeguard if dataclass init is auto-used
|
||||
assert isinstance(self.timestamp, datetime)
|
||||
assert isinstance(self.luminosity, int)
|
||||
assert isinstance(self.sound_level, float)
|
||||
assert isinstance(self.temperature, float)
|
||||
assert isinstance(self.inference, float)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"LogEntry(time={self.timestamp.isoformat()}, Lx={self.luminosity}, "
|
||||
f"dB={self.sound_level:.1f}, Temp={self.temperature:.1f}, Inference={self.inference:.2f})"
|
||||
)
|
||||
Loading…
Reference in a new issue