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()
|