File size: 3,883 Bytes
b975c79
217c3af
 
 
 
 
 
 
 
b975c79
217c3af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from __future__ import annotations

import json
import os
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict, List, Optional

CONFIG_FILE = Path(__file__).with_name("app-config.json")
DEFAULT_LABELS: List[str] = [
    "Heart",
    "Oblong",
    "Oval",
    "Round",
    "Square",
]
DEFAULT_ALLOWED_ORIGINS: List[str] = [
    "http://localhost:3000",
    "http://127.0.0.1:3000",
]
DEFAULT_PORT = 7860


def _load_config_file() -> Dict[str, Any]:
    if not CONFIG_FILE.exists():
        return {}

    try:
        return json.loads(CONFIG_FILE.read_text())
    except json.JSONDecodeError as exc:
        raise ValueError(f"Konfigurasi JSON tidak valid: {CONFIG_FILE}") from exc


def _parse_labels(raw: Any) -> List[str]:
    if raw is None:
        return DEFAULT_LABELS.copy()

    if isinstance(raw, str):
        parts = [label.strip() for label in raw.split(",") if label.strip()]
        return parts or DEFAULT_LABELS.copy()

    if isinstance(raw, list):
        filtered = [str(label).strip() for label in raw if str(label).strip()]
        return filtered or DEFAULT_LABELS.copy()

    return DEFAULT_LABELS.copy()


def _parse_origins(raw: Any) -> List[str]:
    if raw is None:
        return DEFAULT_ALLOWED_ORIGINS.copy()

    if isinstance(raw, str):
        parts = [origin.strip() for origin in raw.split(",") if origin.strip()]
        return parts or DEFAULT_ALLOWED_ORIGINS.copy()

    if isinstance(raw, list):
        filtered = [str(origin).strip() for origin in raw if str(origin).strip()]
        return filtered or DEFAULT_ALLOWED_ORIGINS.copy()

    return DEFAULT_ALLOWED_ORIGINS.copy()


def _parse_bool(raw: Any, fallback: bool = False) -> bool:
    if raw is None:
        return fallback

    if isinstance(raw, bool):
        return raw

    return str(raw).strip().lower() in {"1", "true", "yes", "on"}


def _parse_int(raw: Any, fallback: int) -> int:
    if raw is None:
        return fallback

    try:
        value = int(raw)
    except (TypeError, ValueError):
        return fallback

    return value if value > 0 else fallback


def _resolve_model_path(file_value: Optional[str]) -> str:
    env_value = os.environ.get("MODEL_PATH")
    candidate = env_value or file_value or "model/best_model.keras"
    path = Path(candidate)
    if not path.is_absolute():
        path = (CONFIG_FILE.parent / path).resolve()
    return str(path)


@dataclass(frozen=True)
class Settings:
    model_path: str
    labels: List[str]
    port: int
    cors_allowed_origins: List[str]
    gradio_auth_token: str | None
    gradio_username: str | None
    gradio_password: str | None
    share: bool


_CONFIG_CACHE = _load_config_file()


@lru_cache(maxsize=1)
def get_settings() -> Settings:
    labels_raw = os.environ.get("MODEL_LABELS")
    origins_raw = os.environ.get("CORS_ALLOWED_ORIGINS")
    share_raw: Any = os.environ.get("GRADIO_SHARE")
    port_raw: Any = os.environ.get("PORT")

    return Settings(
        model_path=_resolve_model_path(_CONFIG_CACHE.get("model_path")),
        labels=_parse_labels(labels_raw if labels_raw is not None else _CONFIG_CACHE.get("labels")),
        port=_parse_int(port_raw if port_raw is not None else _CONFIG_CACHE.get("port"), DEFAULT_PORT),
        cors_allowed_origins=_parse_origins(
            origins_raw if origins_raw is not None else _CONFIG_CACHE.get("cors_allowed_origins")
        ),
        gradio_auth_token=os.environ.get("GRADIO_AUTH_TOKEN") or _CONFIG_CACHE.get("gradio_auth_token"),
        gradio_username=os.environ.get("GRADIO_USERNAME") or _CONFIG_CACHE.get("gradio_username"),
        gradio_password=os.environ.get("GRADIO_PASSWORD") or _CONFIG_CACHE.get("gradio_password"),
        share=_parse_bool(share_raw if share_raw is not None else _CONFIG_CACHE.get("share"), False),
    )


settings = get_settings()