mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-19 23:08:08 +02:00
Docs refactor (#22703)
* add generation script a script to read yaml code blocks from docs markdown files and generate corresponding "Frigate UI" tab instructions based on the json schema, i18n, section configs (hidden fields), and nav mappings * first pass * components * add to gitignore * second pass * fix broken anchors * fixes * clean up tabs * version bump * tweaks * remove role mapping config from ui
This commit is contained in:
130
docs/scripts/lib/section_config_parser.py
Normal file
130
docs/scripts/lib/section_config_parser.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""Parse TypeScript section config files for hidden/advanced field info."""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
SECTION_CONFIGS_DIR = (
|
||||
Path(__file__).resolve().parents[3]
|
||||
/ "web"
|
||||
/ "src"
|
||||
/ "components"
|
||||
/ "config-form"
|
||||
/ "section-configs"
|
||||
)
|
||||
|
||||
|
||||
def _extract_string_array(text: str, field_name: str) -> list[str]:
|
||||
"""Extract a string array value from TypeScript object literal text."""
|
||||
pattern = rf"{field_name}\s*:\s*\[(.*?)\]"
|
||||
match = re.search(pattern, text, re.DOTALL)
|
||||
if not match:
|
||||
return []
|
||||
content = match.group(1)
|
||||
return re.findall(r'"([^"]*)"', content)
|
||||
|
||||
|
||||
def _parse_section_file(filepath: Path) -> dict[str, Any]:
|
||||
"""Parse a single section config .ts file."""
|
||||
text = filepath.read_text()
|
||||
|
||||
# Extract base block
|
||||
base_match = re.search(r"base\s*:\s*\{(.*?)\n \}", text, re.DOTALL)
|
||||
base_text = base_match.group(1) if base_match else ""
|
||||
|
||||
# Extract global block
|
||||
global_match = re.search(r"global\s*:\s*\{(.*?)\n \}", text, re.DOTALL)
|
||||
global_text = global_match.group(1) if global_match else ""
|
||||
|
||||
# Extract camera block
|
||||
camera_match = re.search(r"camera\s*:\s*\{(.*?)\n \}", text, re.DOTALL)
|
||||
camera_text = camera_match.group(1) if camera_match else ""
|
||||
|
||||
result: dict[str, Any] = {
|
||||
"fieldOrder": _extract_string_array(base_text, "fieldOrder"),
|
||||
"hiddenFields": _extract_string_array(base_text, "hiddenFields"),
|
||||
"advancedFields": _extract_string_array(base_text, "advancedFields"),
|
||||
}
|
||||
|
||||
# Merge global-level hidden fields
|
||||
global_hidden = _extract_string_array(global_text, "hiddenFields")
|
||||
if global_hidden:
|
||||
result["globalHiddenFields"] = global_hidden
|
||||
|
||||
# Merge camera-level hidden fields
|
||||
camera_hidden = _extract_string_array(camera_text, "hiddenFields")
|
||||
if camera_hidden:
|
||||
result["cameraHiddenFields"] = camera_hidden
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def load_section_configs() -> dict[str, dict[str, Any]]:
|
||||
"""Load all section configs from TypeScript files.
|
||||
|
||||
Returns:
|
||||
Dict mapping section name to parsed config.
|
||||
"""
|
||||
# Read sectionConfigs.ts to get the mapping of section keys to filenames
|
||||
registry_path = SECTION_CONFIGS_DIR.parent / "sectionConfigs.ts"
|
||||
registry_text = registry_path.read_text()
|
||||
|
||||
configs: dict[str, dict[str, Any]] = {}
|
||||
|
||||
for ts_file in SECTION_CONFIGS_DIR.glob("*.ts"):
|
||||
if ts_file.name == "types.ts":
|
||||
continue
|
||||
|
||||
section_name = ts_file.stem
|
||||
configs[section_name] = _parse_section_file(ts_file)
|
||||
|
||||
# Map section config keys from the registry (handles renames like
|
||||
# "timestamp_style: timestampStyle")
|
||||
key_map: dict[str, str] = {}
|
||||
for match in re.finditer(
|
||||
r"(\w+)(?:\s*:\s*\w+)?\s*,", registry_text[registry_text.find("{") :]
|
||||
):
|
||||
key = match.group(1)
|
||||
key_map[key] = key
|
||||
|
||||
# Handle explicit key mappings like `timestamp_style: timestampStyle`
|
||||
for match in re.finditer(r"(\w+)\s*:\s*(\w+)\s*,", registry_text):
|
||||
key_map[match.group(1)] = match.group(2)
|
||||
|
||||
return configs
|
||||
|
||||
|
||||
def get_hidden_fields(
|
||||
configs: dict[str, dict[str, Any]],
|
||||
section_key: str,
|
||||
level: str = "global",
|
||||
) -> set[str]:
|
||||
"""Get the set of hidden fields for a section at a given level.
|
||||
|
||||
Args:
|
||||
configs: Loaded section configs
|
||||
section_key: Config section name (e.g., "record")
|
||||
level: "global" or "camera"
|
||||
|
||||
Returns:
|
||||
Set of hidden field paths (e.g., {"enabled_in_config", "sync_recordings"})
|
||||
"""
|
||||
config = configs.get(section_key, {})
|
||||
hidden = set(config.get("hiddenFields", []))
|
||||
|
||||
if level == "global":
|
||||
hidden.update(config.get("globalHiddenFields", []))
|
||||
elif level == "camera":
|
||||
hidden.update(config.get("cameraHiddenFields", []))
|
||||
|
||||
return hidden
|
||||
|
||||
|
||||
def get_advanced_fields(
|
||||
configs: dict[str, dict[str, Any]],
|
||||
section_key: str,
|
||||
) -> set[str]:
|
||||
"""Get the set of advanced fields for a section."""
|
||||
config = configs.get(section_key, {})
|
||||
return set(config.get("advancedFields", []))
|
||||
Reference in New Issue
Block a user