Clean up unittest attempt.

This commit is contained in:
Kylian Schmidt
2025-09-08 12:53:16 +02:00
parent 657b273975
commit 9e59d06796
22 changed files with 17 additions and 7476 deletions
+23
View File
@@ -0,0 +1,23 @@
import zipfile
import datetime
from pathlib import Path
WEB_FOLDER = Path("plots")
BACKUP_FOLDER = Path("backups")
today = datetime.date.today().strftime("%Y%m%d")
backup_name = f"backup-{today}.zip"
backup_path = BACKUP_FOLDER / backup_name
BACKUP_FOLDER.mkdir(parents=True, exist_ok=True)
if backup_path.exists():
print(f"Backup already exists: {backup_path}")
else:
print(f"Creating backup: {backup_path}")
with zipfile.ZipFile(backup_path, "w", zipfile.ZIP_DEFLATED) as zipf:
for path in WEB_FOLDER.rglob("*"):
if path.is_file():
arcname = path.relative_to(WEB_FOLDER.parent)
zipf.write(path, arcname)
print("✅ Backup complete.")
+145
View File
@@ -0,0 +1,145 @@
"""
Scientific Gallery Configuration Management
This module provides dataclasses and utilities for managing configuration
of the scientific gallery system, including paths, gallery settings,
UI preferences, and data sources.
"""
from dataclasses import dataclass, field, asdict
from pathlib import Path
import yaml
@dataclass
class PathConfig:
"""Configuration for system paths and directories."""
work_dir: str
web_folder: str
@dataclass
class GalleryConfig:
"""Configuration for gallery generation and display settings."""
plot_root: str
png_dpi: int
backup_folder: str
@dataclass
class UIConfig:
"""Configuration for user interface behavior and preferences."""
max_recent_plots: int
search_debounce_ms: int
@dataclass
class MetadataConfig:
"""Configuration for metadata handling."""
cache_enabled: bool = True
inherit_from_parent: bool = True
supported_formats: list[str] = field(
default_factory=lambda: ['.yaml', '.yml', '.json']
)
@dataclass
class GalleryItem:
"""Represents a single data source for the gallery."""
name: str
path: Path
@dataclass
class Config:
"""
Main configuration class that aggregates all gallery settings.
Provides backward compatibility properties and methods for loading
configuration from YAML files.
"""
paths: PathConfig
gallery: GalleryConfig
ui: UIConfig
metadata: MetadataConfig
sources: list[GalleryItem] = field(default_factory=list)
@property
def web_folder(self):
"""Backward compatibility property for web folder path."""
return self.paths.web_folder
@property
def png_dpi(self):
"""Backward compatibility property for PNG conversion DPI."""
return self.gallery.png_dpi
@property
def plot_root(self):
"""Backward compatibility property for plot root directory."""
return self.gallery.plot_root
@property
def backup_folder(self):
"""Backward compatibility property for backup folder path."""
return self.gallery.backup_folder
@classmethod
def from_yaml(cls, yaml_file: str) -> "Config":
"""
Load configuration from a YAML file.
Args:
yaml_file: Path to the YAML configuration file
Returns:
Config instance with loaded settings
Raises:
FileNotFoundError: If the YAML file doesn't exist
yaml.YAMLError: If the YAML file is malformed
"""
with open(yaml_file, "r") as f:
data = yaml.safe_load(f)
paths_data = data.get('paths', {})
gallery_data = data.get('gallery', {})
ui_data = data.get('ui', {})
metadata_data = data.get('metadata', {})
sources_data = data.get('sources', [])
paths = PathConfig(**paths_data)
gallery = GalleryConfig(**gallery_data)
ui = UIConfig(**ui_data)
metadata = MetadataConfig(**metadata_data)
sources = [
GalleryItem(name=source["name"], path=Path(source["path"]))
for source in sources_data
]
return cls(
paths=paths,
gallery=gallery,
ui=ui,
metadata=metadata,
sources=sources
)
def to_yaml(self, yaml_file: str) -> None:
"""
Save the current configuration to a YAML file.
Args:
yaml_file: Path where to save the YAML configuration
Raises:
IOError: If unable to write to the specified file
"""
with open(yaml_file, "w") as f:
yaml.dump(asdict(self), f, default_flow_style=False)
if __name__ == "__main__":
config = Config.from_yaml("config.yaml")
print("Loaded config successfully:", config)
+159
View File
@@ -0,0 +1,159 @@
"""
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}")
raise e
def load_folder_metadata(folder_path: Path) -> Dict[str, Any]:
"""
Load folder-level metadata from metadata.yaml, metadata.yml, or metadata.json.
Args:
folder_path: Path to the folder to check for metadata
Returns:
Dictionary containing the folder metadata
"""
# Try YAML first, then JSON for backwards compatibility
for filename in ['metadata.yaml', 'metadata.yml', 'metadata.json']:
metadata_path = folder_path / filename
if metadata_path.exists():
return load_metadata_file(metadata_path)
return {}
def get_metadata_file_path(folder_path: Path) -> str:
"""
Get the metadata file path for a folder. Returns existing file if found,
otherwise suggests metadata.yaml (preferred format).
Args:
folder_path: Path to the folder to check for metadata
Returns:
String path to the metadata file (existing or suggested)
"""
# Preferred order: YAML first, then JSON
preferred_files = ['metadata.yaml', 'metadata.yml', 'metadata.json']
for filename in preferred_files:
metadata_path = folder_path / filename
if metadata_path.exists():
return str(metadata_path)
# If no file exists, suggest metadata.yaml (preferred format)
return str(folder_path / 'metadata.yaml')
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}")