Utilities - rotalabs-comply
Description: AI compliance and audit logging infrastructure

Skip to content

# Utilities Module

Helper functions for date formatting, statistics calculation, and JSON serialization.

## Period Formatting

### format_period

    def format_period(start: datetime, end: datetime) -> str

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

    def parse_period(period: str) -> Tuple[datetime, datetime]

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

    def calculate_statistics(entries: Sequence[Any]) -> Dict[str, Any]

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

    def severity_weight(severity: Union[str, RiskLevel]) -> int

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

    def json_serializer(obj: Any) -> Any

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

    def dump_json(obj: Any, **kwargs) -> str

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

    def load_json(data: str) -> Any

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,
    )