- Add expand_filename_template() to yaml_config.py
- Supports {author}, {from}, {to}, {date}, {ext} placeholders
- {date} formats as DD_MM_YYYY (e.g. 31_03_2026)
- Spaces in author replaced with underscores for safe filenames
- Unknown placeholders left as-is
- Add comprehensive CONFIG.md documentation covering YAML setup, migration, and template syntax
102 lines
2.7 KiB
Python
102 lines
2.7 KiB
Python
import logging
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_ENV_VAR_RE = re.compile(r"\$\{([^}]+)\}")
|
|
|
|
|
|
def resolve_env_vars(value: str) -> str:
|
|
"""Replace ${VAR} patterns with environment variable values.
|
|
|
|
Non-recursive: if ${A} expands to a literal ${B}, ${B} is NOT resolved.
|
|
Unknown variables are replaced with empty string and a warning is logged.
|
|
"""
|
|
if not isinstance(value, str):
|
|
return value
|
|
|
|
def _replacer(match: re.Match) -> str:
|
|
var_name = match.group(1)
|
|
env_value = os.environ.get(var_name)
|
|
if env_value is None:
|
|
logger.warning("Environment variable %s is not set, using empty string", var_name)
|
|
return ""
|
|
return env_value
|
|
|
|
return _ENV_VAR_RE.sub(_replacer, value)
|
|
|
|
|
|
def ensure_config_dir(path: Path | None = None) -> Path:
|
|
"""Create ~/.config/redmine-reporter/ with 0o700 permissions.
|
|
|
|
Returns the path to the config directory.
|
|
No-op if the directory already exists.
|
|
"""
|
|
if path is None:
|
|
path = Path.home() / ".config" / "redmine-reporter"
|
|
path.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
return path
|
|
|
|
|
|
def check_file_permissions(path: Path) -> list[str]:
|
|
"""Check that a config file has safe permissions (0o600 or stricter).
|
|
|
|
Returns a list of warning messages. Empty list means no issues.
|
|
"""
|
|
warnings: list[str] = []
|
|
if not path.exists():
|
|
return warnings
|
|
|
|
mode = path.stat().st_mode & 0o777
|
|
if mode > 0o600:
|
|
warnings.append(
|
|
f"Config file permissions are too open ({mode:04o}). "
|
|
f"Expected 0600. Fix with: chmod 600 {path}"
|
|
)
|
|
return warnings
|
|
|
|
|
|
def expand_filename_template(
|
|
template: str,
|
|
*,
|
|
author: str = "",
|
|
from_date: str = "",
|
|
to_date: str = "",
|
|
ext: str = "",
|
|
) -> str:
|
|
"""Expand placeholders in a filename template.
|
|
|
|
Supported placeholders:
|
|
{author} — author name
|
|
{from} — start date (YYYY-MM-DD)
|
|
{to} — end date (YYYY-MM-DD)
|
|
{date} — end date formatted as DD_MM_YYYY
|
|
{ext} — file extension without dot
|
|
|
|
Unknown placeholders are left as-is.
|
|
"""
|
|
date_dd_mm_yyyy = ""
|
|
if to_date:
|
|
try:
|
|
from datetime import datetime
|
|
|
|
dt = datetime.strptime(to_date, "%Y-%m-%d")
|
|
date_dd_mm_yyyy = dt.strftime("%d_%m_%Y")
|
|
except ValueError:
|
|
date_dd_mm_yyyy = to_date
|
|
|
|
replacements = {
|
|
"author": author.replace(" ", "_"),
|
|
"from": from_date,
|
|
"to": to_date,
|
|
"date": date_dd_mm_yyyy,
|
|
"ext": ext,
|
|
}
|
|
|
|
result = template
|
|
for key, value in replacements.items():
|
|
result = result.replace("{" + key + "}", value)
|
|
return result
|