Source code for xraylabtool.io.data_export
"""
Data export and formatting utilities for XRayLabTool.
This module contains functions for formatting and exporting calculation results
in various formats suitable for different use cases.
"""
import json
from typing import Any
import numpy as np
[docs]
def format_xray_result(
result: Any,
format_type: str = "table",
fields: list[str] | None = None,
precision: int = 6,
) -> str:
"""
Format XRayResult for display or export.
Args:
result: XRayResult object to format
format_type: Output format ('table', 'csv', 'json')
fields: Specific fields to include (None for all)
precision: Decimal precision for numeric values
Returns:
Formatted string representation
"""
if format_type.lower() == "json":
return _format_as_json(result, fields, precision)
elif format_type.lower() == "csv":
return _format_as_csv(result, fields, precision)
else: # table format
return _format_as_table(result, fields, precision)
def _format_as_json(result: Any, fields: list[str] | None, precision: int) -> str:
"""Format result as JSON string."""
data = {}
# Get all attributes if fields not specified
# (exclude methods and private attributes)
if fields is None:
fields = [
attr
for attr in dir(result)
if not attr.startswith("_") and not callable(getattr(result, attr))
]
for field in fields:
if hasattr(result, field):
value = getattr(result, field)
if isinstance(value, np.ndarray):
# Convert numpy arrays to lists with proper precision
data[field] = np.round(value, precision).tolist()
elif isinstance(value, float | np.floating):
data[field] = round(float(value), precision)
else:
data[field] = value
return json.dumps(data, indent=2)
def _format_as_csv(result: Any, fields: list[str] | None, precision: int) -> str:
"""Format result as CSV string."""
if fields is None:
fields = [
attr
for attr in dir(result)
if not attr.startswith("_") and not callable(getattr(result, attr))
]
# Create DataFrame for consistent formatting
data: dict[str, Any] = {}
for field in fields:
if hasattr(result, field):
value = getattr(result, field)
if isinstance(value, np.ndarray):
data[field] = np.round(value, precision)
elif isinstance(value, float | np.floating):
data[field] = [round(float(value), precision)]
else:
data[field] = (
[value] if not isinstance(value, list | np.ndarray) else value
)
# Ensure all arrays have the same length
max_length = max(
len(v) if isinstance(v, list | np.ndarray) else 1 for v in data.values()
)
for key, value in data.items():
if not isinstance(value, list | np.ndarray):
data[key] = [value] * max_length
elif len(value) == 1 and max_length > 1:
data[key] = [value[0]] * max_length
# Lazy import pandas only when needed
import pandas as pd
df = pd.DataFrame(data)
return df.to_csv(index=False)
def _format_as_table(result: Any, fields: list[str] | None, precision: int) -> str:
"""Format result as human-readable table."""
if fields is None:
fields = [
attr
for attr in dir(result)
if not attr.startswith("_") and not callable(getattr(result, attr))
]
lines = []
lines.append(f"XRay Properties for {getattr(result, 'formula', 'Unknown')}")
lines.append("=" * 50)
for field in fields:
if hasattr(result, field):
value = getattr(result, field)
if isinstance(value, np.ndarray):
if len(value) == 1:
lines.append(f"{field}: {value[0]:.{precision}f}")
else:
lines.append(f"{field}: [{len(value)} values]")
range_str = (
f" Range: {value.min():.{precision}f} - "
f"{value.max():.{precision}f}"
)
lines.append(range_str)
elif isinstance(value, float | np.floating):
lines.append(f"{field}: {value:.{precision}f}")
else:
lines.append(f"{field}: {value}")
return "\n".join(lines)
[docs]
def format_calculation_summary(results: list[Any], format_type: str = "table") -> str:
"""
Format a summary of multiple calculation results.
Args:
results: List of XRayResult objects
format_type: Output format type
Returns:
Formatted summary string
"""
if not results:
return "No results to display"
if format_type.lower() == "json":
return json.dumps(
[json.loads(format_xray_result(r, "json")) for r in results], indent=2
)
elif format_type.lower() == "csv":
# Combine all results into a single CSV
all_data = []
for result in results:
result_dict = {}
for attr in dir(result):
if not attr.startswith("_"):
value = getattr(result, attr)
if isinstance(value, np.ndarray) and len(value) == 1:
result_dict[attr] = value[0]
elif not isinstance(value, np.ndarray):
result_dict[attr] = value
all_data.append(result_dict)
# Lazy import pandas only when needed
import pandas as pd
df = pd.DataFrame(all_data)
return df.to_csv(index=False)
else:
# Table format - show summary statistics
lines = [f"Summary of {len(results)} calculations", "=" * 40]
for i, result in enumerate(results, 1):
formula = getattr(result, "formula", f"Result {i}")
lines.append(f"{i}. {formula}")
return "\n".join(lines)