Utilities Module¶
Helper functions for date formatting, statistics calculation, and JSON serialization.
Period Formatting¶
format_period¶
Format a date range as a human-readable period string.
Automatically detects the most appropriate format based on the date range.
Parameters:
| Parameter | Type | Description |
|---|---|---|
start |
datetime |
Start of period |
end |
datetime |
End of period |
Returns: Formatted period string
Examples:
from datetime import datetime
from rotalabs_comply.utils import format_period
# Quarter
format_period(datetime(2026, 1, 1), datetime(2026, 3, 31))
# Returns: '2026-Q1'
# Month
format_period(datetime(2026, 1, 1), datetime(2026, 1, 31))
# Returns: 'Jan 2026'
# Year
format_period(datetime(2026, 1, 1), datetime(2026, 12, 31))
# Returns: '2026'
# Date range
format_period(datetime(2026, 1, 15), datetime(2026, 2, 20))
# Returns: '2026-01-15 to 2026-02-20'
parse_period¶
Parse a period string back to start and end datetimes.
Parameters:
| Parameter | Type | Description |
|---|---|---|
period |
str |
Period string to parse |
Returns: Tuple of (start_datetime, end_datetime)
Raises: ValueError if the period string cannot be parsed
Supported formats:
- YYYY-Q# (quarter)
- YYYY (year)
- Mon YYYY or Month YYYY (month)
- YYYY-MM-DD to YYYY-MM-DD (date range)
- ISO date format
Examples:
from rotalabs_comply.utils import parse_period
# Quarter
start, end = parse_period("2026-Q1")
# start: datetime(2026, 1, 1, 0, 0)
# end: datetime(2026, 3, 31, 23, 59, 59)
# Month
start, end = parse_period("Jan 2026")
# start: datetime(2026, 1, 1, 0, 0)
# end: datetime(2026, 1, 31, 23, 59, 59)
# Year
start, end = parse_period("2026")
# start: datetime(2026, 1, 1, 0, 0)
# end: datetime(2026, 12, 31, 23, 59, 59)
# Date range
start, end = parse_period("2026-01-15 to 2026-02-20")
# start: datetime(2026, 1, 15, 0, 0)
# end: datetime(2026, 2, 20, 23, 59, 59)
Statistics¶
calculate_statistics¶
Calculate basic statistics from audit entries.
Parameters:
| Parameter | Type | Description |
|---|---|---|
entries |
Sequence[Any] |
List of audit entries (dict or object) |
Expected entry attributes:
- timestamp
- safety_passed
- latency_ms
- provider
- model
- detectors_triggered
Returns:
| Key | Type | Description |
|---|---|---|
total_entries |
int |
Total count |
safety_passed |
int |
Entries passing safety |
safety_failed |
int |
Entries failing safety |
safety_rate |
float |
Pass percentage (0-100) |
avg_latency_ms |
float |
Average latency |
min_latency_ms |
float |
Minimum latency |
max_latency_ms |
float |
Maximum latency |
providers |
Dict[str, int] |
Provider counts |
models |
Dict[str, int] |
Model counts |
detectors |
Dict[str, int] |
Detector trigger counts |
Example:
from rotalabs_comply.utils import calculate_statistics
entries = [
{"safety_passed": True, "latency_ms": 100.0, "provider": "openai"},
{"safety_passed": True, "latency_ms": 150.0, "provider": "openai"},
{"safety_passed": False, "latency_ms": 200.0, "provider": "anthropic"},
]
stats = calculate_statistics(entries)
print(stats["total_entries"]) # 3
print(stats["safety_rate"]) # 66.67
print(stats["providers"]) # {"openai": 2, "anthropic": 1}
group_by_date¶
def group_by_date(
entries: Sequence[Any],
granularity: Literal["day", "week", "month", "quarter", "year"] = "day",
) -> Dict[str, List[Any]]
Group audit entries by date with configurable granularity.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
entries |
Sequence[Any] |
Required | Entries with timestamp |
granularity |
str |
"day" |
Time granularity |
Granularity options:
| Value | Key Format | Example |
|---|---|---|
"day" |
YYYY-MM-DD |
2026-01-15 |
"week" |
YYYY-WNN |
2026-W03 |
"month" |
YYYY-MM |
2026-01 |
"quarter" |
YYYY-Q# |
2026-Q1 |
"year" |
YYYY |
2026 |
Returns: Dictionary mapping period keys to lists of entries (sorted by key)
Example:
from rotalabs_comply.utils import group_by_date
entries = [
{"timestamp": "2026-01-15T10:00:00"},
{"timestamp": "2026-01-15T14:00:00"},
{"timestamp": "2026-01-16T09:00:00"},
]
# By day
grouped = group_by_date(entries, granularity="day")
# {'2026-01-15': [entry1, entry2], '2026-01-16': [entry3]}
# By month
grouped = group_by_date(entries, granularity="month")
# {'2026-01': [entry1, entry2, entry3]}
severity_weight¶
Convert a severity level to a numeric weight for scoring.
Parameters:
| Parameter | Type | Description |
|---|---|---|
severity |
Union[str, RiskLevel] |
Severity level |
Returns: Numeric weight (1-10)
Weight mapping:
| Severity | Weight |
|---|---|
critical |
10 |
high |
5 |
medium |
2 |
low |
1 |
info |
1 |
Example:
from rotalabs_comply.utils import severity_weight
from rotalabs_comply.frameworks.base import RiskLevel
severity_weight("critical") # 10
severity_weight("high") # 5
severity_weight(RiskLevel.MEDIUM) # 2
JSON Serialization¶
json_serializer¶
Custom JSON serializer for compliance-specific types.
Handles:
- datetime -> ISO format string
- Enum -> .value
- Dataclasses -> dict via asdict()
- Pydantic models -> .model_dump() or .dict()
- Objects with to_dict() method
- Objects with __dict__
- bytes -> UTF-8 decoded string
- set -> list
- timedelta -> total seconds
Raises: TypeError if object cannot be serialized
Example:
import json
from datetime import datetime
from rotalabs_comply.utils import json_serializer
from rotalabs_comply.frameworks.base import RiskLevel
data = {
"timestamp": datetime(2026, 1, 15, 10, 30),
"status": RiskLevel.HIGH,
}
json.dumps(data, default=json_serializer)
# '{"timestamp": "2026-01-15T10:30:00", "status": "high"}'
dump_json¶
Serialize object to JSON string with compliance type support.
Convenience wrapper around json.dumps with json_serializer.
Parameters:
| Parameter | Type | Description |
|---|---|---|
obj |
Any |
Object to serialize |
**kwargs |
Arguments passed to json.dumps |
Example:
from datetime import datetime
from rotalabs_comply.utils import dump_json
data = {"created": datetime(2026, 1, 15)}
print(dump_json(data))
# {"created": "2026-01-15T00:00:00"}
print(dump_json(data, indent=2))
# {
# "created": "2026-01-15T00:00:00"
# }
load_json¶
Parse JSON string with datetime restoration.
Automatically converts ISO format strings back to datetime objects.
Parameters:
| Parameter | Type | Description |
|---|---|---|
data |
str |
JSON string to parse |
Returns: Parsed Python object with datetimes restored
Example:
from rotalabs_comply.utils import load_json
data = '{"timestamp": "2026-01-15T10:30:00"}'
obj = load_json(data)
print(type(obj["timestamp"]))
# <class 'datetime.datetime'>
Usage Examples¶
Complete Statistics Pipeline¶
from datetime import datetime, timedelta
from rotalabs_comply.utils import (
format_period,
calculate_statistics,
group_by_date,
dump_json,
)
# Sample entries
entries = [...]
# Calculate overall statistics
stats = calculate_statistics(entries)
print(f"Total: {stats['total_entries']}")
print(f"Safety Rate: {stats['safety_rate']}%")
# Group by time
daily = group_by_date(entries, granularity="day")
weekly = group_by_date(entries, granularity="week")
# Analyze trends
for period, period_entries in daily.items():
period_stats = calculate_statistics(period_entries)
print(f"{period}: {period_stats['total_entries']} entries")
# Export to JSON
report = {
"period": format_period(
datetime(2026, 1, 1),
datetime(2026, 3, 31)
),
"statistics": stats,
"daily_breakdown": {
period: calculate_statistics(entries)
for period, entries in daily.items()
},
}
json_output = dump_json(report, indent=2)
Period-Based Reporting¶
from rotalabs_comply.utils import format_period, parse_period
# Format for display
period_str = format_period(start, end)
print(f"Reporting Period: {period_str}")
# Parse from user input
user_input = "2026-Q1"
start, end = parse_period(user_input)
# Generate report for parsed period
report = await generator.generate(
period_start=start,
period_end=end,
profile=profile,
)