File size: 3,030 Bytes
e515383
 
 
efc36ce
 
 
 
 
 
 
 
e515383
 
efc36ce
e515383
 
 
 
 
 
 
5e55742
efc36ce
 
e515383
 
efc36ce
5e55742
 
 
 
e515383
efc36ce
e515383
 
efc36ce
5e55742
e515383
 
 
 
 
 
 
efc36ce
e515383
 
efc36ce
5e55742
e515383
 
efc36ce
e515383
 
efc36ce
5e55742
 
e515383
 
 
 
 
 
 
 
 
 
efc36ce
e515383
 
 
 
efc36ce
e515383
 
efc36ce
 
e515383
 
 
 
 
 
efc36ce
b7549df
605dd3b
e515383
 
efc36ce
 
e515383
efc36ce
 
e515383
 
efc36ce
e515383
 
5e55742
e515383
efc36ce
 
 
 
 
 
e515383
 
efc36ce
 
e515383
 
efc36ce
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
104
105
106
107
108
109
110
111
112
113
114
from pathlib import Path
from typing import Literal, List
from pydantic import BaseModel
from pydantic_settings import (
    BaseSettings,
    DotEnvSettingsSource,
    EnvSettingsSource,
    SettingsConfigDict,
    PydanticBaseSettingsSource,
    YamlConfigSettingsSource,
)
import yaml


def join_tag(loader, node):
    """
    Help joining pathes in config.YAML directly.
    """
    parts = loader.construct_sequence(node)
    path = Path(*(str(part) for part in parts)).resolve()
    return str(path)


# It didn't work before, After some research, .SafeLoaded is unmentioned must for my case.
yaml.SafeLoader.add_constructor("!join", join_tag)


class IntervalsConfig(BaseModel):
    system_metrics_seconds: float
    frames_summary_every: int
    realtime_updates_every: float


class YoloConfig(BaseModel):
    """Contains yolo configurations"""

    model_name: str
    classes: List[str]
    batch_size: int
    epochs: int
    wandb: bool
    augment: bool
    data_path: str


class SecurityDetector(BaseModel):
    "Contains Security Detectors like Smoke - Fire"

    model_name: str
    classes: List[str]


class DepthConfig(BaseModel):
    "Contains depths estimation configurations"

    model_name: str
    device: Literal["cuda", "cpu"]
    encoder: Literal["vits", "vitb", "vitl", "vitg"]


class AppConfig(BaseSettings):
    """
    Main app Configuration
    - Gets defaults from config.yaml (via load_config)
    - Override values with .env
    """

    # Note that it doesn't show error, Take care.
    model_config = SettingsConfigDict(
        env_file=Path(__file__).parent / ".env",
        env_file_encoding="utf-8",
        yaml_file=Path(__file__).parent / "config.yaml",
        extra="ignore",  # Ignore other settings in yaml and env as they are not mentioedhere
    )

    project_name: str
    project_desc: str
    task: Literal["indoor", "outdoor"]

    yolo: YoloConfig
    security_detector: SecurityDetector
    depth: DepthConfig
    intervals: IntervalsConfig
    redis_url: str
    dagshub_user_token: str
    experiment: bool

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls: type[BaseSettings],  # Base param.
        **kwargs,
    ) -> tuple[PydanticBaseSettingsSource, ...]:
        """
        Once you use this, no need to use load_config, it is already the same.
        But this time it fixs the priority part, order by parameters priority.
        """

        # Order by priority (first, more important)
        return (
            DotEnvSettingsSource(settings_cls),  # Most important
            EnvSettingsSource(
                settings_cls
            ),  # This allow for ex. hugging face to override .env values with its values.
            YamlConfigSettingsSource(settings_cls),
        )  # The return must be a tuple


if __name__ == "__main__":
    # Trying to checking both yaml and .env.   This works really fine now.
    config = AppConfig()
    print(config.model_dump())
    print(config.model_dump()["project_name"])