File size: 2,890 Bytes
c7822c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
from __future__ import annotations

import json
import logging
import math
import platform
import time
from pathlib import Path
from typing import Any, Iterable

import numpy as np


LOGGER_NAME = "egg_damage"


def get_logger(name: str | None = None) -> logging.Logger:
    logger = logging.getLogger(name or LOGGER_NAME)
    if not logger.handlers:
        handler = logging.StreamHandler()
        handler.setFormatter(
            logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
        )
        logger.addHandler(handler)
    logger.setLevel(logging.INFO)
    return logger


def save_json(data: Any, path: str | Path) -> None:
    path = Path(path)
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8") as f:
        json.dump(make_json_safe(data), f, indent=2)


def load_json(path: str | Path) -> Any:
    with Path(path).open("r", encoding="utf-8") as f:
        return json.load(f)


def make_json_safe(value: Any) -> Any:
    if isinstance(value, dict):
        return {str(k): make_json_safe(v) for k, v in value.items()}
    if isinstance(value, (list, tuple)):
        return [make_json_safe(v) for v in value]
    if isinstance(value, Path):
        return str(value)
    if isinstance(value, (np.integer,)):
        return int(value)
    if isinstance(value, (np.floating,)):
        value = float(value)
        return value if math.isfinite(value) else None
    if isinstance(value, float):
        return value if math.isfinite(value) else None
    if isinstance(value, np.ndarray):
        return value.tolist()
    return value


def now_stamp() -> str:
    return time.strftime("%Y%m%d-%H%M%S")


def model_file_size_mb(path: str | Path | None) -> float | None:
    if not path:
        return None
    p = Path(path)
    if not p.exists():
        return None
    return p.stat().st_size / (1024 * 1024)


def timer() -> float:
    return time.perf_counter()


def elapsed_ms(start: float, count: int = 1) -> float:
    if count <= 0:
        return 0.0
    return (time.perf_counter() - start) * 1000.0 / count


def format_distribution(rows: Iterable[dict[str, Any]]) -> str:
    lines = ["split | label | count", "--- | --- | ---"]
    for row in rows:
        lines.append(f"{row['split']} | {row['label']} | {row['count']}")
    return "\n".join(lines)


def environment_summary() -> dict[str, Any]:
    summary = {"python": platform.python_version(), "platform": platform.platform()}
    try:
        import torch

        summary["torch"] = torch.__version__
        summary["cuda_available"] = bool(torch.cuda.is_available())
        summary["cuda_device"] = torch.cuda.get_device_name(0) if torch.cuda.is_available() else None
    except Exception:
        summary["torch"] = "not importable"
        summary["cuda_available"] = False
        summary["cuda_device"] = None
    return summary