Files
ETPlot/orchestration/metadata.py
T
Kylian Schmidt 72ebc5e103 feat: Implement metadata system and reorganize project structure
 Features:
- Add comprehensive metadata system with YAML/JSON support
- Implement hierarchical metadata inheritance from parent folders
- Support plot-specific metadata overrides
- Add metadata caching for performance optimization

🗂️ Code Organization:
- Move Python orchestration code to orchestration/ folder
- Move validation utilities to tests/ folder
- Move documentation to docs/ folder
- Separate metadata functionality into dedicated module

🔧 Infrastructure:
- Add automatic asset copying to web directory
- Fix asset path resolution for nested directories
- Update template to use dynamic asset paths
- Add MetadataConfig class with inheritance options

📚 Documentation:
- Add comprehensive metadata usage guide (METADATA_USAGE.md)
- Add implementation documentation (METADATA_IMPLEMENTATION.md)
- Include example metadata files in examples/
- Add metadata validation utility script

🐛 Bug Fixes:
- Fix breadcrumb navigation and JavaScript functionality
- Resolve asset path issues in nested directories
- Update template imports for modular CSS/JS structure

This commit introduces a flexible metadata system that allows users to add
rich metadata to plots and folders using YAML or JSON files, with full
hierarchical inheritance and plot-specific overrides. The project structure
is now better organized with clear separation of concerns.
2025-07-08 12:33:49 +02:00

133 lines
4.1 KiB
Python

"""
Metadata Management for Scientific Gallery Generator
This module handles loading, parsing, and caching of metadata for plots
and folders in the gallery system. Supports YAML and JSON formats with
hierarchical inheritance.
Features:
- Load metadata from YAML/JSON files
- Hierarchical metadata inheritance from parent folders
- Plot-specific metadata overrides
- Metadata caching for performance
"""
import json
import yaml
from pathlib import Path
from typing import Dict, Any
def load_metadata_file(metadata_path: Path) -> Dict[str, Any]:
"""
Load metadata from a YAML or JSON file.
Args:
metadata_path: Path to the metadata file
Returns:
Dictionary containing the metadata, empty dict if file doesn't exist
or can't be parsed
"""
if not metadata_path.exists():
return {}
try:
with metadata_path.open('r', encoding='utf-8') as f:
suffix_lower = metadata_path.suffix.lower()
if suffix_lower == '.yaml' or suffix_lower == '.yml':
return yaml.safe_load(f) or {}
elif metadata_path.suffix.lower() == '.json':
return json.load(f) or {}
else:
print(f"Warning: Unknown metadata file format: "
f"{metadata_path}")
return {}
except (yaml.YAMLError, json.JSONDecodeError, IOError) as e:
print(f"Warning: Could not parse metadata file {metadata_path}: {e}")
return {}
def load_folder_metadata(folder_path: Path) -> Dict[str, Any]:
"""
Load folder-level metadata from meta.yaml or meta.json.
Args:
folder_path: Path to the folder to check for metadata
Returns:
Dictionary containing the folder metadata
"""
# Try YAML first, then JSON
for filename in ['meta.yaml', 'meta.yml', 'meta.json']:
metadata_path = folder_path / filename
if metadata_path.exists():
return load_metadata_file(metadata_path)
return {}
def merge_metadata(parent_metadata: Dict[str, Any],
child_metadata: Dict[str, Any]) -> Dict[str, Any]:
"""
Merge parent and child metadata, with child values overriding parent
values.
Args:
parent_metadata: Metadata from parent folder
child_metadata: Metadata from current folder
Returns:
Merged metadata dictionary
"""
merged = parent_metadata.copy()
merged.update(child_metadata)
return merged
def resolve_metadata_for_plot(plot_path: Path,
inherited_metadata: Dict[str, Any]
) -> Dict[str, Any]:
"""
Resolve metadata for a specific plot by merging inherited metadata
with plot-specific metadata.
Args:
plot_path: Path to the plot file (PDF)
inherited_metadata: Metadata inherited from folder hierarchy
Returns:
Final merged metadata for the plot
"""
plot_stem = plot_path.stem
plot_dir = plot_path.parent
# Check for plot-specific metadata files
for suffix in ['.yaml', '.yml', '.json']:
plot_metadata_path = plot_dir / f"{plot_stem}{suffix}"
if plot_metadata_path.exists():
plot_metadata = load_metadata_file(plot_metadata_path)
return merge_metadata(inherited_metadata, plot_metadata)
# No plot-specific metadata found, return inherited metadata
return inherited_metadata.copy()
def save_metadata_cache(web_dir: Path,
plot_metadata_cache: Dict[str, Dict[str, Any]]
) -> None:
"""
Save plot metadata cache to meta_cache.json in the web directory.
Args:
web_dir: Web directory where the cache file should be saved
plot_metadata_cache: Dictionary mapping plot names to their metadata
"""
cache_path = web_dir / "meta_cache.json"
try:
with cache_path.open('w', encoding='utf-8') as f:
json.dump(plot_metadata_cache, f, indent=2, ensure_ascii=False)
print(f"Saved metadata cache: {cache_path}")
except IOError as e:
print(f"Warning: Could not save metadata cache {cache_path}: {e}")