# This file has been generated - DO NOT MODIFY
# API Version : 10.2.0

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional, Protocol, Union, runtime_checkable
from uuid import UUID

import pandas as pd
from pydantic import BaseModel, ConfigDict, Field, RootModel
from typing_extensions import Annotated

# generated by datamodel-codegen:
#   filename:  input.json


class AdviceParameters(BaseModel):
    """
    Parameters for advice job.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]


class AnalysisStatus(Enum):
    started = "started"
    done = "done"


class ClosestRatePercentageThresholdItem(RootModel[float]):
    root: Annotated[float, Field(ge=0.0, le=100.0)]


class ClosestRateRatioThresholdItem(RootModel[float]):
    root: Annotated[float, Field(ge=0.0, le=1.0)]


class ClusterStats(BaseModel):
    n_active_tasks: Annotated[Optional[int], Field(title="N Active Tasks")] = None
    available_concurrency: Annotated[
        Optional[int], Field(title="Available Concurrency")
    ] = None
    utilization_rate_100: Annotated[
        Optional[int], Field(title="Utilization Rate 100")
    ] = None
    status_message: Annotated[Optional[str], Field(title="Status Message")] = ""


class ColumnStats(BaseModel):
    label: Annotated[str, Field(title="Label")]
    dtype: Annotated[str, Field(title="Dtype")]
    missing_number: Annotated[int, Field(ge=0, title="Missing Number")]
    missing_proportion: Annotated[
        float, Field(ge=0.0, le=1.0, title="Missing Proportion")
    ]
    unique_number: Annotated[int, Field(ge=0, title="Unique Number")]
    unique_proportion: Annotated[
        float, Field(ge=0.0, le=1.0, title="Unique Proportion")
    ]
    mean: Annotated[Optional[float], Field(title="Mean")] = None
    median: Annotated[Optional[float], Field(title="Median")] = None
    standard_deviation: Annotated[
        Optional[float], Field(title="Standard Deviation")
    ] = None
    mode: Annotated[Optional[Union[str, int, float]], Field(title="Mode")] = None
    is_skewed: Annotated[Optional[bool], Field(title="Is Skewed")] = None
    is_rare: Annotated[Optional[bool], Field(title="Is Rare")] = None


class ColumnType(Enum):
    int = "int"
    bool = "bool"
    category = "category"
    float = "float"
    datetime = "datetime"


class CompatibilityStatus(Enum):
    compatible = "compatible"
    incompatible = "incompatible"
    unknown = "unknown"


class Contributions(BaseModel):
    data: Annotated[Dict[str, Dict[str, float]], Field(title="Data")]


class DatasetSummary(BaseModel):
    stats: Annotated[List[ColumnStats], Field(title="Stats")]
    probable_ids: Annotated[List[str], Field(title="Probable Ids")]


class ExcludeVariablesMethod(Enum):
    """
    The method to exclude column.

    There are several possible choices:

    - ``row_order``: **SENSITIVE** The excluded column will be linked to the original row order.
    This is a violation of privacy.

    - ``coordinate_similarity``: The excluded column will be linked by individual similarity.
    """

    row_order = "row_order"
    coordinate_similarity = "coordinate_similarity"


class ExcludeVariablesParameters(BaseModel):
    """
    Parameters to exclude some variables from the anonymization before re-assigning.

    This parameter can be applied to any variable, regardless of its type but it is particularly
    recommended when the data contains categorical variables with a large number of modalities.
    """

    variable_names: Annotated[
        List[str],
        Field(
            description="List of variables to exclude from the anonymization. ",
            title="Variable Names",
        ),
    ]
    replacement_strategy: Annotated[
        ExcludeVariablesMethod,
        Field(description="See :class:`ExcludeVariablesMethod`."),
    ]


class ExplainedVariance(BaseModel):
    raw: Annotated[List[float], Field(title="Raw")]
    ratio: Annotated[List[float], Field(title="Ratio")]


class ProjectionType(Enum):
    fpca = "fpca"


class FPCAParameters(BaseModel):
    """
    Parameters of a FPCA projection.
    """

    nf: Annotated[
        int,
        Field(
            description="Number of degrees of freedom kept by the projection.",
            title="Nf",
        ),
    ]
    projection_type: Annotated[ProjectionType, Field(title="Projection Type")] = (
        ProjectionType.fpca
    )


class FileType(Enum):
    csv = "csv"
    parquet = "parquet"


class ProjectionType1(Enum):
    flatten = "flatten"


class FlattenParameters(BaseModel):
    """
    Parameters of a Flatten projection.
    """

    projection_type: Annotated[ProjectionType1, Field(title="Projection Type")] = (
        ProjectionType1.flatten
    )


class ForgottenPasswordRequest(BaseModel):
    email: Annotated[str, Field(title="Email")]


class GenericParameters(BaseModel):
    pass
    model_config = ConfigDict(
        extra="allow",
    )


class GenericResult(BaseModel):
    pass


class ProjectionType2(Enum):
    geolocation_density = "geolocation_density"


class GeolocationDensityParameters(BaseModel):
    """
    Parameters of a Geolocation Density projection.
    """

    projection_type: Annotated[ProjectionType2, Field(title="Projection Type")] = (
        ProjectionType2.geolocation_density
    )
    n_lon_to_compute: Annotated[
        Optional[int],
        Field(description="Number of longitude to compute.", title="N Lon To Compute"),
    ] = 100
    n_lat_to_compute: Annotated[
        Optional[int],
        Field(description="Number of latitude to compute.", title="N Lat To Compute"),
    ] = 100


class GeolocationFeatures(Enum):
    """
    Features available for geolocation feature projection and metrics.
    """

    length = "length"
    duration = "duration"
    speed = "speed"


class ProjectionType3(Enum):
    geolocation_features = "geolocation_features"


class GeolocationFeaturesParameters(BaseModel):
    """
    Parameters of a Geolocation Features projection.
    """

    projection_type: Annotated[ProjectionType3, Field(title="Projection Type")] = (
        ProjectionType3.geolocation_features
    )


class GeolocationPrivacyMetrics(BaseModel):
    local_cloaking: Annotated[float, Field(title="Local Cloaking")]
    hidden_rate: Annotated[float, Field(title="Hidden Rate")]
    specific_hidden_rate: Annotated[
        Optional[float], Field(title="Specific Hidden Rate")
    ] = None
    distance_to_closest: Annotated[
        Optional[float], Field(title="Distance To Closest")
    ] = None
    closest_distances_ratio: Annotated[
        Optional[float], Field(title="Closest Distances Ratio")
    ] = None
    closest_rate: Annotated[Optional[float], Field(title="Closest Rate")] = None
    features_inference_rate: Annotated[
        Optional[float], Field(title="Features Inference Rate")
    ] = None
    features_correlation_protection_rate: Annotated[
        Optional[float], Field(title="Features Correlation Protection Rate")
    ] = None
    thresholded_signal_inference_rate: Annotated[
        Optional[float], Field(title="Thresholded Signal Inference Rate")
    ] = None
    trace_correlation_protection_rate: Annotated[
        Optional[float], Field(title="Trace Correlation Protection Rate")
    ] = None
    inference_distances: Annotated[
        Optional[List[float]], Field(title="Inference Distances")
    ] = None


class TrainingFractionItem(RootModel[float]):
    root: Annotated[float, Field(ge=0.0, le=1.0)]


class ImputeMethod(Enum):
    knn = "knn"
    mode = "mode"
    median = "median"
    mean = "mean"
    fast_knn = "fast_knn"


class JobKind(Enum):
    avatarization = "avatarization"
    privacy_metrics = "privacy_metrics"
    signal_metrics = "signal_metrics"
    avatarization_with_time_series = "avatarization_with_time_series"
    avatarization_multi_table = "avatarization_multi_table"
    privacy_metrics_with_time_series = "privacy_metrics_with_time_series"
    privacy_metrics_multi_table = "privacy_metrics_multi_table"
    signal_metrics_with_time_series = "signal_metrics_with_time_series"
    privacy_metrics_geolocation = "privacy_metrics_geolocation"
    generic = "generic"
    advice = "advice"


class JobProgress(BaseModel):
    completion_rate_100: Annotated[int, Field(title="Completion Rate 100")]
    name: Annotated[Optional[str], Field(title="Name")] = None
    created_at: Annotated[datetime, Field(title="Created At")]


class JobStatus(Enum):
    pending = "pending"
    started = "started"
    success = "success"
    failure = "failure"
    killed = "killed"
    unknown = "unknown"


class LinkMethod(Enum):
    """
    Available assignment methods to link a child to its parent table after the anonymization.

    LINEAR_SUM_ASSIGNMENT: Assign using the linear sum assignment algorithm.
    This method is a good privacy and utility trade-off. The algorithm consumes lots of resources.

    MINIMUM_DISTANCE_ASSIGNMENT: Assign using the minimum distance assignment algorithm.
    This method assigns the closest child to the parent. It is an acceptable privacy and utility
    trade-off.
    This algorithm consumes less resources than the linear sum assignment.

    SENSITIVE_ORIGINAL_ORDER_ASSIGNMENT: Assign the child to the parent using the original order.
    WARNING!!! This method is a HIGH PRIVACY BREACH as it keeps the original order to assign
    the child to the parent.
    This method isn't recommended for privacy reasons but consumes less resources than the other
    methods.
    """

    linear_sum_assignment = "linear_sum_assignment"
    minimum_distance_assignment = "minimum_distance_assignment"
    sensitive_original_order_assignment = "sensitive_original_order_assignment"


class LoginResponse(BaseModel):
    access_token: Annotated[str, Field(title="Access Token")]
    refresh_token: Annotated[Optional[str], Field(title="Refresh Token")] = None
    token_type: Annotated[str, Field(title="Token Type")]


class PointOfInterest(Enum):
    """
    Available target points of interest (POI) for geolocation signal-based metrics.
    """

    start = "start"
    end = "end"


class PrivacyMetricsComputationType(Enum):
    standalone = "standalone"
    to_bottom_id_propagated = "to_bottom_id_propagated"
    to_top_enriched = "to_top_enriched"
    full_enriched = "full_enriched"


class ParameterType(Enum):
    privacy_metrics_geolocation = "privacy_metrics_geolocation"


class MetricsType(Enum):
    multi_table = "multi_table"


class MetricsType1(Enum):
    time_series = "time_series"


class PrivacyMetricsTargets(BaseModel):
    hidden_rate: Annotated[str, Field(title="Hidden Rate")]
    local_cloaking: Annotated[str, Field(title="Local Cloaking")]
    distance_to_closest: Annotated[str, Field(title="Distance To Closest")]
    closest_distances_ratio: Annotated[str, Field(title="Closest Distances Ratio")]
    column_direct_match_protection: Annotated[
        str, Field(title="Column Direct Match Protection")
    ]
    categorical_hidden_rate: Annotated[str, Field(title="Categorical Hidden Rate")]
    row_direct_match_protection: Annotated[
        str, Field(title="Row Direct Match Protection")
    ]
    correlation_protection_rate: Annotated[
        str, Field(title="Correlation Protection Rate")
    ]
    inference_continuous: Annotated[str, Field(title="Inference Continuous")]
    inference_categorical: Annotated[str, Field(title="Inference Categorical")]
    closest_rate: Annotated[str, Field(title="Closest Rate")]


class ParameterType1(Enum):
    privacy_metrics_time_series = "privacy_metrics_time_series"


class PrivacyMetricsTimeSeriesParameters(BaseModel):
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]
    unshuffled_avatar_dataset_id: Annotated[
        UUID, Field(title="Unshuffled Avatar Dataset Id")
    ]
    projection_parameters: Annotated[
        Optional[Union[FPCAParameters, FlattenParameters]],
        Field(
            description="Parameters of the time series projection.",
            title="Projection Parameters",
        ),
    ] = None
    parameter_type: Annotated[ParameterType1, Field(title="Parameter Type")] = (
        ParameterType1.privacy_metrics_time_series
    )


class Projections(BaseModel):
    records: Annotated[List[List[float]], Field(title="Records")]
    avatars: Annotated[List[List[float]], Field(title="Avatars")]


class Report(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    user_id: Annotated[UUID, Field(title="User Id")]
    job_id: Annotated[UUID, Field(title="Job Id")]
    created_at: Annotated[Optional[datetime], Field(title="Created At")] = None
    download_url: Annotated[str, Field(title="Download Url")]


class ReportCreate(BaseModel):
    avatarization_job_id: Annotated[UUID, Field(title="Avatarization Job Id")]
    privacy_job_id: Annotated[UUID, Field(title="Privacy Job Id")]
    signal_job_id: Annotated[UUID, Field(title="Signal Job Id")]


class ReportFromDataCreate(BaseModel):
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]
    avatars_dataset_id: Annotated[UUID, Field(title="Avatars Dataset Id")]
    privacy_job_id: Annotated[UUID, Field(title="Privacy Job Id")]
    signal_job_id: Annotated[UUID, Field(title="Signal Job Id")]


class ReportGeolocationPrivacyCreate(BaseModel):
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]
    avatars_dataset_id: Annotated[UUID, Field(title="Avatars Dataset Id")]
    privacy_job_id: Annotated[UUID, Field(title="Privacy Job Id")]


class ResetPasswordRequest(BaseModel):
    email: Annotated[str, Field(title="Email")]
    new_password: Annotated[str, Field(title="New Password")]
    new_password_repeated: Annotated[str, Field(title="New Password Repeated")]
    token: Annotated[UUID, Field(title="Token")]


class SignalMetricsParameters(BaseModel):
    """
    Parameters to configure a SignalMetricsJob.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    original_id: Annotated[UUID, Field(title="Original Id")]
    avatarization_job_id: Annotated[
        Optional[UUID], Field(title="Avatarization Job Id")
    ] = None
    avatars_id: Annotated[UUID, Field(title="Avatars Id")]


class SignalMetricsTargets(BaseModel):
    hellinger_mean: Annotated[str, Field(title="Hellinger Mean")]
    correlation_difference_ratio: Annotated[
        str, Field(title="Correlation Difference Ratio")
    ]


class SignalMetricsTimeSeriesParameters(BaseModel):
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]
    avatar_dataset_id: Annotated[UUID, Field(title="Avatar Dataset Id")]


class SignalMetricsWithTimeSeriesParameters(BaseModel):
    model_config = ConfigDict(
        extra="allow",
    )
    vanilla_original_id: Annotated[
        Optional[UUID],
        Field(
            description="Identifier of the original vanilla dataset.",
            title="Vanilla Original Id",
        ),
    ] = None
    vanilla_avatars_id: Annotated[
        Optional[UUID],
        Field(
            description="Identifier of the avatar vanilla dataset.",
            title="Vanilla Avatars Id",
        ),
    ] = None
    time_series: Annotated[
        List[SignalMetricsTimeSeriesParameters],
        Field(
            description="Privacy metrics parameters of the time series. Each time series can have a different set of parameters.",
            title="Time Series",
        ),
    ]
    seed: Annotated[
        Optional[int],
        Field(description="Seed used to generate the random state.", title="Seed"),
    ] = None


class SignalPosition(Enum):
    """
    Available known signal positions for geolocation signal-based metrics.
    """

    start = "start"
    middle = "middle"
    end = "end"


class TableReference(BaseModel):
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]
    is_individual_level: Annotated[
        bool,
        Field(
            description="Whether the table contains rows at the individual level. Individual level means that each row contains information about a distinct physical person.",
            title="Is Individual Level",
        ),
    ]


class TimeSeriesSignalMetricsPerDataset(BaseModel):
    pointwise_hellinger_mean: Annotated[float, Field(title="Pointwise Hellinger Mean")]
    pointwise_hellinger_std: Annotated[float, Field(title="Pointwise Hellinger Std")]
    density_hellinger_mean: Annotated[float, Field(title="Density Hellinger Mean")]
    density_hellinger_std: Annotated[float, Field(title="Density Hellinger Std")]
    original_id: Annotated[UUID, Field(title="Original Id")]
    avatars_id: Annotated[UUID, Field(title="Avatars Id")]


class UserRole(Enum):
    admin = "admin"
    user = "user"


class ValidationError(BaseModel):
    loc: Annotated[List[Union[str, int]], Field(title="Location")]
    msg: Annotated[str, Field(title="Message")]
    type: Annotated[str, Field(title="Error Type")]


class GrantTypeItem(RootModel[str]):
    root: Annotated[str, Field(pattern="password")]


class Login(BaseModel):
    grant_type: Annotated[Optional[GrantTypeItem], Field(title="Grant Type")] = None
    username: Annotated[str, Field(title="Username")]
    password: Annotated[str, Field(title="Password")]
    scope: Annotated[Optional[str], Field(title="Scope")] = ""
    client_id: Annotated[Optional[str], Field(title="Client Id")] = None
    client_secret: Annotated[Optional[str], Field(title="Client Secret")] = None


class CreateDataset(BaseModel):
    file: Annotated[bytes, Field(title="File")]
    name: Annotated[Optional[str], Field(title="Name")] = None


class GenericJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[GenericResult] = None
    parameters: GenericParameters
    current_progress: Optional[JobProgress] = None


class AdviceJobCreate(BaseModel):
    parameters: AdviceParameters


class AvatarizationTimeSeriesParameters(BaseModel):
    """
    Parameters for the anonymization of a Time Series.
    """

    dataset_id: Annotated[
        UUID,
        Field(
            description="Identifier of the dataset containing the time series.",
            title="Dataset Id",
        ),
    ]
    projection_parameters: Annotated[
        Union[FPCAParameters, FlattenParameters],
        Field(
            description="Parameters of the time series projection.",
            title="Projection Parameters",
        ),
    ]


class ColumnDetail(BaseModel):
    type: ColumnType
    label: Annotated[str, Field(title="Label")]
    is_identifier: Annotated[Optional[bool], Field(title="Is Identifier")] = False


class CompatibilityResponse(BaseModel):
    message: Annotated[str, Field(title="Message")]
    most_recent_compatible_client: Annotated[
        Optional[str], Field(title="Most Recent Compatible Client")
    ] = None
    status: CompatibilityStatus


class CreateUser(BaseModel):
    """
    Create a user, either with an email, or a username.

    The choice will depend on how your server is setup.
    """

    username: Annotated[Optional[str], Field(title="Username")] = None
    email: Annotated[Optional[str], Field(title="Email")] = None
    role: Optional[UserRole] = UserRole.user
    password: Annotated[Optional[str], Field(title="Password")] = None


class Dataset(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    hash: Annotated[str, Field(title="Hash")]
    name: Annotated[Optional[str], Field(title="Name")] = None
    columns: Annotated[List[ColumnDetail], Field(title="Columns")]
    download_url: Annotated[str, Field(title="Download Url")]
    analysis_status: Optional[AnalysisStatus] = None
    analysis_duration: Annotated[Optional[float], Field(title="Analysis Duration")] = (
        None
    )
    nb_lines: Annotated[Optional[int], Field(title="Nb Lines")] = None
    nb_dimensions: Annotated[int, Field(title="Nb Dimensions")]
    summary: Optional[DatasetSummary] = None
    filetype: FileType


class HTTPValidationError(BaseModel):
    detail: Annotated[Optional[List[ValidationError]], Field(title="Detail")] = None


class ImputationParameters(BaseModel):
    method: Optional[ImputeMethod] = None
    k: Annotated[Optional[int], Field(title="K")] = None
    training_fraction: Annotated[
        Optional[TrainingFractionItem], Field(title="Training Fraction")
    ] = None


class PrivacyMetrics(BaseModel):
    hidden_rate: Annotated[float, Field(title="Hidden Rate")]
    local_cloaking: Annotated[float, Field(title="Local Cloaking")]
    distance_to_closest: Annotated[
        Optional[float], Field(title="Distance To Closest")
    ] = None
    closest_distances_ratio: Annotated[
        Optional[float], Field(title="Closest Distances Ratio")
    ] = None
    column_direct_match_protection: Annotated[
        float, Field(title="Column Direct Match Protection")
    ]
    categorical_hidden_rate: Annotated[
        Optional[float], Field(title="Categorical Hidden Rate")
    ] = None
    row_direct_match_protection: Annotated[
        float, Field(title="Row Direct Match Protection")
    ]
    correlation_protection_rate: Annotated[
        Optional[float], Field(title="Correlation Protection Rate")
    ] = None
    inference_continuous: Annotated[
        Optional[float], Field(title="Inference Continuous")
    ] = None
    inference_categorical: Annotated[
        Optional[float], Field(title="Inference Categorical")
    ] = None
    closest_rate: Annotated[Optional[float], Field(title="Closest Rate")] = None
    targets: PrivacyMetricsTargets


class PrivacyMetricsGeolocationScenario(BaseModel):
    projection_parameters: Annotated[
        Union[GeolocationDensityParameters, GeolocationFeaturesParameters],
        Field(
            description="Parameters of the geolocation projection.",
            title="Projection Parameters",
        ),
    ]
    known_features: Annotated[
        Optional[List[GeolocationFeatures]],
        Field(description="Known geolocation features", title="Known Features"),
    ] = None
    target_feature: Annotated[
        Optional[GeolocationFeatures], Field(description="Target geolocation features")
    ] = None
    known_signal_position: Annotated[
        Optional[SignalPosition], Field(description="Position of the known signal")
    ] = None
    target_signal_poi: Annotated[
        Optional[PointOfInterest],
        Field(description="Target point of interest (POI) to infer"),
    ] = None
    inference_metric_threshold: Annotated[
        Optional[float],
        Field(
            description="Threshold (in meters) for the inference metric",
            title="Inference Metric Threshold",
        ),
    ] = 500


class PrivacyMetricsGeolocationScenarioResult(BaseModel):
    privacy_metrics: GeolocationPrivacyMetrics
    parameters: PrivacyMetricsGeolocationScenario


class PrivacyMetricsParameters(BaseModel):
    """
    Parameters to configure a PrivacyMetricsJob.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    original_id: Annotated[UUID, Field(title="Original Id")]
    avatarization_job_id: Annotated[
        Optional[UUID], Field(title="Avatarization Job Id")
    ] = None
    imputation: Annotated[
        Optional[ImputationParameters],
        Field(
            description="Imputation parameters used to preprocess avatars data for metrics computation."
        ),
    ] = None
    ncp: Annotated[
        Optional[int],
        Field(
            description="Number of components used for distance based metrics computation. Default behavior similar to avatarization. If unspecified, ncp is set to the minimum value between 5 and the number of variables after one hot encoding. Note: we recommend using to use the same ncp as during the avatarization to check the worst-case scenario.",
            title="Ncp",
        ),
    ] = None
    use_categorical_reduction: Annotated[
        Optional[bool],
        Field(
            description="Parameter to vectorize categorical variables using cat2vec. See :class:`AvatarizationParameters` for more details. Note: we recommend using the same parameter as during the avatarization.",
            title="Use Categorical Reduction",
        ),
    ] = None
    known_variables: Annotated[
        Optional[List[str]],
        Field(
            description="Variables considered to be known by the attacker. Used to measure some specific privacy metrics. Note: we recommend using demographic variables as they are the most likely to be known by an attacker.",
            title="Known Variables",
        ),
    ] = None
    target: Annotated[
        Optional[str],
        Field(
            description="The target variable to be inferred. Used to measure some specific privacy metrics. Must be used together with ``known_variables``. Note: we recommend using a sensitive variable",
            title="Target",
        ),
    ] = None
    closest_rate_percentage_threshold: Annotated[
        Optional[ClosestRatePercentageThresholdItem],
        Field(
            description="Threshold of closest original individuals to define a distance threshold. Any avatar data generated closer from an original than the distance threshold, is considered at risk.",
            title="Closest Rate Percentage Threshold",
        ),
    ] = None
    closest_rate_ratio_threshold: Annotated[
        Optional[ClosestRateRatioThresholdItem],
        Field(
            description="Closest distance ratio threshold below which an avatar data is considered too close to an original and too isolated from the rest to be safe.",
            title="Closest Rate Ratio Threshold",
        ),
    ] = None
    categorical_hidden_rate_variables: Annotated[
        Optional[List[str]],
        Field(
            description="List of categorical variables to compute the categorical hidden rate.",
            title="Categorical Hidden Rate Variables",
        ),
    ] = []
    unshuffled_avatars_id: Annotated[UUID, Field(title="Unshuffled Avatars Id")]
    seed: Annotated[
        Optional[int],
        Field(description="Seed used to generate the random state.", title="Seed"),
    ] = None


class PrivacyMetricsPerMultiTableDataset(BaseModel):
    hidden_rate: Annotated[float, Field(title="Hidden Rate")]
    local_cloaking: Annotated[float, Field(title="Local Cloaking")]
    distance_to_closest: Annotated[
        Optional[float], Field(title="Distance To Closest")
    ] = None
    closest_distances_ratio: Annotated[
        Optional[float], Field(title="Closest Distances Ratio")
    ] = None
    column_direct_match_protection: Annotated[
        float, Field(title="Column Direct Match Protection")
    ]
    categorical_hidden_rate: Annotated[
        Optional[float], Field(title="Categorical Hidden Rate")
    ] = None
    row_direct_match_protection: Annotated[
        float, Field(title="Row Direct Match Protection")
    ]
    correlation_protection_rate: Annotated[
        Optional[float], Field(title="Correlation Protection Rate")
    ] = None
    inference_continuous: Annotated[
        Optional[float], Field(title="Inference Continuous")
    ] = None
    inference_categorical: Annotated[
        Optional[float], Field(title="Inference Categorical")
    ] = None
    closest_rate: Annotated[Optional[float], Field(title="Closest Rate")] = None
    targets: PrivacyMetricsTargets
    original_id: Annotated[UUID, Field(title="Original Id")]
    unshuffled_avatars_id: Annotated[UUID, Field(title="Unshuffled Avatars Id")]
    metrics_type: Annotated[MetricsType, Field(title="Metrics Type")] = (
        MetricsType.multi_table
    )
    individual_identifier_variable: Annotated[
        str, Field(title="Individual Identifier Variable")
    ]
    computation_type: PrivacyMetricsComputationType
    dataset_name: Annotated[str, Field(title="Dataset Name")]


class PrivacyMetricsPerTimeSeriesDataset(BaseModel):
    hidden_rate: Annotated[float, Field(title="Hidden Rate")]
    local_cloaking: Annotated[float, Field(title="Local Cloaking")]
    distance_to_closest: Annotated[
        Optional[float], Field(title="Distance To Closest")
    ] = None
    closest_distances_ratio: Annotated[
        Optional[float], Field(title="Closest Distances Ratio")
    ] = None
    column_direct_match_protection: Annotated[
        float, Field(title="Column Direct Match Protection")
    ]
    categorical_hidden_rate: Annotated[
        Optional[float], Field(title="Categorical Hidden Rate")
    ] = None
    row_direct_match_protection: Annotated[
        float, Field(title="Row Direct Match Protection")
    ]
    correlation_protection_rate: Annotated[
        Optional[float], Field(title="Correlation Protection Rate")
    ] = None
    inference_continuous: Annotated[
        Optional[float], Field(title="Inference Continuous")
    ] = None
    inference_categorical: Annotated[
        Optional[float], Field(title="Inference Categorical")
    ] = None
    closest_rate: Annotated[Optional[float], Field(title="Closest Rate")] = None
    targets: PrivacyMetricsTargets
    original_id: Annotated[UUID, Field(title="Original Id")]
    unshuffled_avatars_id: Annotated[UUID, Field(title="Unshuffled Avatars Id")]
    metrics_type: Annotated[MetricsType1, Field(title="Metrics Type")] = (
        MetricsType1.time_series
    )


class PrivacyMetricsWithTimeSeriesParameters(BaseModel):
    model_config = ConfigDict(
        extra="allow",
    )
    imputation: Annotated[
        Optional[ImputationParameters],
        Field(
            description="Imputation parameters used to preprocess avatars data for metrics computation."
        ),
    ] = None
    ncp: Annotated[
        Optional[int],
        Field(
            description="Number of components used for distance based metrics computation. Default behavior similar to avatarization. If unspecified, ncp is set to the minimum value between 5 and the number of variables after one hot encoding. Note: we recommend using to use the same ncp as during the avatarization to check the worst-case scenario.",
            title="Ncp",
        ),
    ] = None
    use_categorical_reduction: Annotated[
        Optional[bool],
        Field(
            description="Parameter to vectorize categorical variables using cat2vec. See :class:`AvatarizationParameters` for more details. Note: we recommend using the same parameter as during the avatarization.",
            title="Use Categorical Reduction",
        ),
    ] = None
    known_variables: Annotated[
        Optional[List[str]],
        Field(
            description="Variables considered to be known by the attacker. Used to measure some specific privacy metrics. Note: we recommend using demographic variables as they are the most likely to be known by an attacker.",
            title="Known Variables",
        ),
    ] = None
    target: Annotated[
        Optional[str],
        Field(
            description="The target variable to be inferred. Used to measure some specific privacy metrics. Must be used together with ``known_variables``. Note: we recommend using a sensitive variable",
            title="Target",
        ),
    ] = None
    closest_rate_percentage_threshold: Annotated[
        Optional[ClosestRatePercentageThresholdItem],
        Field(
            description="Threshold of closest original individuals to define a distance threshold. Any avatar data generated closer from an original than the distance threshold, is considered at risk.",
            title="Closest Rate Percentage Threshold",
        ),
    ] = None
    closest_rate_ratio_threshold: Annotated[
        Optional[ClosestRateRatioThresholdItem],
        Field(
            description="Closest distance ratio threshold below which an avatar data is considered too close to an original and too isolated from the rest to be safe.",
            title="Closest Rate Ratio Threshold",
        ),
    ] = None
    categorical_hidden_rate_variables: Annotated[
        Optional[List[str]],
        Field(
            description="List of categorical variables to compute the categorical hidden rate.",
            title="Categorical Hidden Rate Variables",
        ),
    ] = []
    vanilla_original_id: Annotated[
        Optional[UUID],
        Field(
            description="Identifier of the original vanilla dataset.",
            title="Vanilla Original Id",
        ),
    ] = None
    vanilla_unshuffled_avatars_id: Annotated[
        Optional[UUID],
        Field(
            description="Identifier of the avatar vanilla dataset.",
            title="Vanilla Unshuffled Avatars Id",
        ),
    ] = None
    time_series: Annotated[
        List[PrivacyMetricsTimeSeriesParameters],
        Field(
            description="Privacy metrics parameters of the time series. Each time series can have a different set of parameters.",
            title="Time Series",
        ),
    ]
    seed: Annotated[
        Optional[int],
        Field(description="Seed used to generate the random state.", title="Seed"),
    ] = None


class SignalMetrics(BaseModel):
    hellinger_mean: Annotated[float, Field(title="Hellinger Mean")]
    hellinger_std: Annotated[float, Field(title="Hellinger Std")]
    correlation_difference_ratio: Annotated[
        Optional[float], Field(title="Correlation Difference Ratio")
    ] = None
    targets: SignalMetricsTargets


class SignalMetricsJobCreate(BaseModel):
    parameters: SignalMetricsParameters


class SignalMetricsPerDataset(BaseModel):
    hellinger_mean: Annotated[float, Field(title="Hellinger Mean")]
    hellinger_std: Annotated[float, Field(title="Hellinger Std")]
    correlation_difference_ratio: Annotated[
        Optional[float], Field(title="Correlation Difference Ratio")
    ] = None
    targets: SignalMetricsTargets
    original_id: Annotated[UUID, Field(title="Original Id")]
    avatars_id: Annotated[UUID, Field(title="Avatars Id")]


class SignalMetricsWithTimeSeriesJobCreate(BaseModel):
    parameters: SignalMetricsWithTimeSeriesParameters


class TableLink(BaseModel):
    parent_table: Annotated[
        TableReference, Field(description="Reference to the parent table.")
    ]
    child_table: Annotated[
        TableReference, Field(description="Reference to the child table.")
    ]
    parent_link_key: Annotated[
        str,
        Field(
            description="Variable to be used for linking with the child table. The link_key specified here must contain unique values only. It is most often the\n        primary key. It needs to have the is_identifier property set to True when creating\n        the dataset.",
            title="Parent Link Key",
        ),
    ]
    child_link_key: Annotated[
        str,
        Field(
            description="Variable to be used for linking with the parent table. It is most often a foreign key. It needs to have the property is_identifier set to True\n        when creating the dataset.",
            title="Child Link Key",
        ),
    ]
    link_method: Annotated[
        Optional[LinkMethod], Field(description="Assignment method.")
    ] = LinkMethod.linear_sum_assignment


class User(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    organization_id: Annotated[UUID, Field(title="Organization Id")]
    username: Annotated[Optional[str], Field(title="Username")] = None
    email: Annotated[Optional[str], Field(title="Email")] = None
    role: Optional[UserRole] = UserRole.user


class PatchDataset(BaseModel):
    columns: Annotated[Optional[List[ColumnDetail]], Field(title="Columns")] = None


class PrivacyMetricsJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[PrivacyMetrics] = None
    parameters: PrivacyMetricsParameters
    current_progress: Optional[JobProgress] = None


class SignalMetricsJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[SignalMetrics] = None
    parameters: SignalMetricsParameters
    current_progress: Optional[JobProgress] = None


class AvatarizationParameters(BaseModel):
    """
    Parameters to create an anonymization job.

    It contains the seed parameter.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    k: Annotated[int, Field(title="K")]
    column_weights: Annotated[
        Optional[Dict[str, float]], Field(title="Column Weights")
    ] = None
    ncp: Annotated[Optional[int], Field(title="Ncp")] = None
    imputation: Optional[ImputationParameters] = None
    use_categorical_reduction: Annotated[
        Optional[bool], Field(title="Use Categorical Reduction")
    ] = None
    exclude_variables: Optional[ExcludeVariablesParameters] = None
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]
    seed: Annotated[Optional[int], Field(title="Seed")] = None


class AvatarizationResult(BaseModel):
    privacy_metrics: Optional[PrivacyMetrics] = None
    signal_metrics: Optional[SignalMetrics] = None
    avatars_dataset: Dataset
    sensitive_unshuffled_avatars_datasets: Dataset


class AvatarizationResultPerDataset(BaseModel):
    privacy_metrics: Optional[PrivacyMetrics] = None
    signal_metrics: Optional[SignalMetrics] = None
    avatars_dataset: Dataset
    sensitive_unshuffled_avatars_datasets: Dataset
    original_id: Annotated[UUID, Field(title="Original Id")]


class AvatarizationWithTimeSeriesParameters(BaseModel):
    """
    Parameters to create an time series anonymization job.

    It contains the seed parameter.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    k: Annotated[int, Field(title="K")]
    column_weights: Annotated[
        Optional[Dict[str, float]], Field(title="Column Weights")
    ] = None
    ncp: Annotated[Optional[int], Field(title="Ncp")] = None
    imputation: Optional[ImputationParameters] = None
    use_categorical_reduction: Annotated[
        Optional[bool], Field(title="Use Categorical Reduction")
    ] = None
    exclude_variables: Optional[ExcludeVariablesParameters] = None
    vanilla_dataset_id: Annotated[
        Optional[UUID],
        Field(
            description="Dataset identifier of the vanilla data. ",
            title="Vanilla Dataset Id",
        ),
    ] = None
    time_series: Annotated[
        List[AvatarizationTimeSeriesParameters],
        Field(
            description="Parameters of the time series. Each time series can have a different set of parameters.",
            title="Time Series",
        ),
    ]
    seed: Annotated[Optional[int], Field(title="Seed")] = None


class AvatarizationWithTimeSeriesResult(BaseModel):
    datasets: Annotated[List[AvatarizationResultPerDataset], Field(title="Datasets")]


class BaseAvatarizationParameters(BaseModel):
    """
    Parameters to create an anonymization with vanilla dataset.

    This class is used in the multi table job. It does not contain the seed.
    """

    k: Annotated[int, Field(title="K")]
    column_weights: Annotated[
        Optional[Dict[str, float]], Field(title="Column Weights")
    ] = None
    ncp: Annotated[Optional[int], Field(title="Ncp")] = None
    imputation: Optional[ImputationParameters] = None
    use_categorical_reduction: Annotated[
        Optional[bool], Field(title="Use Categorical Reduction")
    ] = None
    exclude_variables: Optional[ExcludeVariablesParameters] = None
    dataset_id: Annotated[UUID, Field(title="Dataset Id")]


class BaseAvatarizationWithTimeSeriesParameters(BaseModel):
    """
    Parameters to configure an anonymization with a time series dataset.

    This class is used in the multi table job. It does not contain the seed.
    """

    k: Annotated[int, Field(title="K")]
    column_weights: Annotated[
        Optional[Dict[str, float]], Field(title="Column Weights")
    ] = None
    ncp: Annotated[Optional[int], Field(title="Ncp")] = None
    imputation: Optional[ImputationParameters] = None
    use_categorical_reduction: Annotated[
        Optional[bool], Field(title="Use Categorical Reduction")
    ] = None
    exclude_variables: Optional[ExcludeVariablesParameters] = None
    vanilla_dataset_id: Annotated[
        Optional[UUID],
        Field(
            description="Dataset identifier of the vanilla data. ",
            title="Vanilla Dataset Id",
        ),
    ] = None
    time_series: Annotated[
        List[AvatarizationTimeSeriesParameters],
        Field(
            description="Parameters of the time series. Each time series can have a different set of parameters.",
            title="Time Series",
        ),
    ]


class BasePrivacyMetricsParameters(BaseModel):
    """
    Parameters to configure privacy metrics.

    This class is used in the multi table job. It does not contain the seed.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    original_id: Annotated[UUID, Field(title="Original Id")]
    avatarization_job_id: Annotated[
        Optional[UUID], Field(title="Avatarization Job Id")
    ] = None
    imputation: Annotated[
        Optional[ImputationParameters],
        Field(
            description="Imputation parameters used to preprocess avatars data for metrics computation."
        ),
    ] = None
    ncp: Annotated[
        Optional[int],
        Field(
            description="Number of components used for distance based metrics computation. Default behavior similar to avatarization. If unspecified, ncp is set to the minimum value between 5 and the number of variables after one hot encoding. Note: we recommend using to use the same ncp as during the avatarization to check the worst-case scenario.",
            title="Ncp",
        ),
    ] = None
    use_categorical_reduction: Annotated[
        Optional[bool],
        Field(
            description="Parameter to vectorize categorical variables using cat2vec. See :class:`AvatarizationParameters` for more details. Note: we recommend using the same parameter as during the avatarization.",
            title="Use Categorical Reduction",
        ),
    ] = None
    known_variables: Annotated[
        Optional[List[str]],
        Field(
            description="Variables considered to be known by the attacker. Used to measure some specific privacy metrics. Note: we recommend using demographic variables as they are the most likely to be known by an attacker.",
            title="Known Variables",
        ),
    ] = None
    target: Annotated[
        Optional[str],
        Field(
            description="The target variable to be inferred. Used to measure some specific privacy metrics. Must be used together with ``known_variables``. Note: we recommend using a sensitive variable",
            title="Target",
        ),
    ] = None
    closest_rate_percentage_threshold: Annotated[
        Optional[ClosestRatePercentageThresholdItem],
        Field(
            description="Threshold of closest original individuals to define a distance threshold. Any avatar data generated closer from an original than the distance threshold, is considered at risk.",
            title="Closest Rate Percentage Threshold",
        ),
    ] = None
    closest_rate_ratio_threshold: Annotated[
        Optional[ClosestRateRatioThresholdItem],
        Field(
            description="Closest distance ratio threshold below which an avatar data is considered too close to an original and too isolated from the rest to be safe.",
            title="Closest Rate Ratio Threshold",
        ),
    ] = None
    categorical_hidden_rate_variables: Annotated[
        Optional[List[str]],
        Field(
            description="List of categorical variables to compute the categorical hidden rate.",
            title="Categorical Hidden Rate Variables",
        ),
    ] = []
    unshuffled_avatars_id: Annotated[UUID, Field(title="Unshuffled Avatars Id")]


class BasePrivacyMetricsWithTimeSeriesParameters(BaseModel):
    """
    Parameters to configure a privacy metric job with a time series dataset.

    This class is used in the multi table job. It does not contain the seed.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    imputation: Annotated[
        Optional[ImputationParameters],
        Field(
            description="Imputation parameters used to preprocess avatars data for metrics computation."
        ),
    ] = None
    ncp: Annotated[
        Optional[int],
        Field(
            description="Number of components used for distance based metrics computation. Default behavior similar to avatarization. If unspecified, ncp is set to the minimum value between 5 and the number of variables after one hot encoding. Note: we recommend using to use the same ncp as during the avatarization to check the worst-case scenario.",
            title="Ncp",
        ),
    ] = None
    use_categorical_reduction: Annotated[
        Optional[bool],
        Field(
            description="Parameter to vectorize categorical variables using cat2vec. See :class:`AvatarizationParameters` for more details. Note: we recommend using the same parameter as during the avatarization.",
            title="Use Categorical Reduction",
        ),
    ] = None
    known_variables: Annotated[
        Optional[List[str]],
        Field(
            description="Variables considered to be known by the attacker. Used to measure some specific privacy metrics. Note: we recommend using demographic variables as they are the most likely to be known by an attacker.",
            title="Known Variables",
        ),
    ] = None
    target: Annotated[
        Optional[str],
        Field(
            description="The target variable to be inferred. Used to measure some specific privacy metrics. Must be used together with ``known_variables``. Note: we recommend using a sensitive variable",
            title="Target",
        ),
    ] = None
    closest_rate_percentage_threshold: Annotated[
        Optional[ClosestRatePercentageThresholdItem],
        Field(
            description="Threshold of closest original individuals to define a distance threshold. Any avatar data generated closer from an original than the distance threshold, is considered at risk.",
            title="Closest Rate Percentage Threshold",
        ),
    ] = None
    closest_rate_ratio_threshold: Annotated[
        Optional[ClosestRateRatioThresholdItem],
        Field(
            description="Closest distance ratio threshold below which an avatar data is considered too close to an original and too isolated from the rest to be safe.",
            title="Closest Rate Ratio Threshold",
        ),
    ] = None
    categorical_hidden_rate_variables: Annotated[
        Optional[List[str]],
        Field(
            description="List of categorical variables to compute the categorical hidden rate.",
            title="Categorical Hidden Rate Variables",
        ),
    ] = []
    vanilla_original_id: Annotated[
        Optional[UUID],
        Field(
            description="Identifier of the original vanilla dataset.",
            title="Vanilla Original Id",
        ),
    ] = None
    vanilla_unshuffled_avatars_id: Annotated[
        Optional[UUID],
        Field(
            description="Identifier of the avatar vanilla dataset.",
            title="Vanilla Unshuffled Avatars Id",
        ),
    ] = None
    time_series: Annotated[
        List[PrivacyMetricsTimeSeriesParameters],
        Field(
            description="Privacy metrics parameters of the time series. Each time series can have a different set of parameters.",
            title="Time Series",
        ),
    ]


class MetaPrivacyMetrics(BaseModel):
    details: Annotated[
        List[
            Union[
                PrivacyMetricsPerMultiTableDataset, PrivacyMetricsPerTimeSeriesDataset
            ]
        ],
        Field(title="Details"),
    ]


class MetaSignalMetrics(BaseModel):
    details: Annotated[
        List[Union[SignalMetricsPerDataset, TimeSeriesSignalMetricsPerDataset]],
        Field(title="Details"),
    ]


class PrivacyMetricsGeolocationParameters(BaseModel):
    """
    Parameters to configure a PrivacyMetricsGeolocationJob.

    It enables to compute privacy metrics for multiple scenarios (with no limit in number).
    """

    model_config = ConfigDict(
        extra="allow",
    )
    original_dataset_id: Annotated[UUID, Field(title="Original Dataset Id")]
    unshuffled_avatar_dataset_id: Annotated[
        UUID, Field(title="Unshuffled Avatar Dataset Id")
    ]
    scenarios: Annotated[
        List[PrivacyMetricsGeolocationScenario], Field(title="Scenarios")
    ]
    parameter_type: Annotated[ParameterType, Field(title="Parameter Type")] = (
        ParameterType.privacy_metrics_geolocation
    )


class PrivacyMetricsGeolocationResult(BaseModel):
    privacy_metrics_per_scenario: Annotated[
        List[PrivacyMetricsGeolocationScenarioResult],
        Field(title="Privacy Metrics Per Scenario"),
    ]


class PrivacyMetricsJobCreate(BaseModel):
    parameters: PrivacyMetricsParameters


class PrivacyMetricsMultiTableParameters(BaseModel):
    """
    Parameters to create a multi table privacy metrics job.

    There are a few rules to follow to make sure that the parameters are consistent.
    The following ones are the same as the ones for the avatarization job.
    Other rules specific to privacy metrics are down below.

    - Every dataset_id in table_parameters must be referenced in table_links.
    - Every dataset_id in table_links must also be referenced in table_parameters.
    - Every TableReference with the same dataset_id must be identical in other fields.
    - There can't be a cycle in the table_links e.g. A -> B -> C -> A is not allowed.
    - Each table needs to have N+1 identifier columns (id, patient_id, visit_id, etc.), where N is
      the  number of direct predecessors of each table. A direct predecessor is a table that has a
      direct link (depth=1) between itself and another table.

      For example, if a table has 2 direct predecessors, it needs to have 3 identifier columns.

      Examples:
        * A -> B <- C , A and C need to have 1 identifier column, and B needs to have 3 identifier
            columns.
        * A -> B -> C <- D , A and D need to have 1 identifier column, B needs to have 2, and
         C needs to only have 3 and not 4, because C only has 2 direct predecessors (B and D).
         There is not a direct link from C to A, so it is not a direct predecessor.
    - Each table can only be linked to the same variable once. This means that if A -> B <- C ,
      A and C need to link to two different columns in B.
    - Every table can only reference another table once. This means that A -> B <- A is
      not possible.
    - Top level tables need to be individual_level tables. For example, A -> B <- C , A and C need
      to be individual_level tables.

      Privacy Metrics specific
      ------------------------

      - The avatar datasets referenced by the avatars_id in table_parameters need to be
        identical in columns, shape, and dtype to their corresponding original dataset.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    table_links: Annotated[
        List[TableLink],
        Field(
            description="Description of each relational link between tables.",
            min_length=1,
            title="Table Links",
        ),
    ]
    table_parameters: Annotated[
        List[
            Union[
                BasePrivacyMetricsParameters, BasePrivacyMetricsWithTimeSeriesParameters
            ]
        ],
        Field(
            description="Privacy metrics parameters for each table. ",
            min_length=1,
            title="Table Parameters",
        ),
    ]
    seed: Annotated[
        Optional[int],
        Field(description="Seed used to generate the random state.", title="Seed"),
    ] = None


class PrivacyMetricsWithTimeSeriesJobCreate(BaseModel):
    parameters: PrivacyMetricsWithTimeSeriesParameters


class AvatarizationJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[AvatarizationResult] = None
    parameters: AvatarizationParameters
    current_progress: Optional[JobProgress] = None


class AvatarizationWithTimeSeriesJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[AvatarizationWithTimeSeriesResult] = None
    parameters: AvatarizationWithTimeSeriesParameters
    current_progress: Optional[JobProgress] = None


class PrivacyMetricsGeolocationJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[PrivacyMetricsGeolocationResult] = None
    parameters: PrivacyMetricsGeolocationParameters
    current_progress: Optional[JobProgress] = None


class PrivacyMetricsMultiTableJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[MetaPrivacyMetrics] = None
    parameters: PrivacyMetricsMultiTableParameters
    current_progress: Optional[JobProgress] = None


class PrivacyMetricsWithTimeSeriesJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[MetaPrivacyMetrics] = None
    parameters: PrivacyMetricsWithTimeSeriesParameters
    current_progress: Optional[JobProgress] = None


class SignalMetricsWithTimeSeriesJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[MetaSignalMetrics] = None
    parameters: SignalMetricsWithTimeSeriesParameters
    current_progress: Optional[JobProgress] = None


class Advice(BaseModel):
    """
    Recommendations on anonymization parameters and processors.
    """

    parameters: Annotated[
        AvatarizationParameters, Field(description="Suggested AvatarizationParameters.")
    ]
    python_client_pipeline: Annotated[
        str,
        Field(
            description="Pipeline ready to run, you need to replace <NAME_OF_YOUR_DF> by the name of your dataframe.",
            title="Python Client Pipeline",
        ),
    ]
    more_details: Annotated[
        Optional[Dict[str, str]],
        Field(
            description="Additional information on parameters and processors.",
            title="More Details",
        ),
    ] = {}


class AvatarizationJobCreate(BaseModel):
    kind: Optional[JobKind] = JobKind.avatarization
    parameters: AvatarizationParameters


class AvatarizationMultiTableParameters(BaseModel):
    """
    Parameters to create a multi table anonymization job.

    There are a few rules to follow to make sure that the parameters are consistent:

    - Every dataset_id in table_parameters must be referenced in table_links.
    - Every dataset_id in table_links must also be referenced in table_parameters.
    - Every TableReference with the same dataset_id must be identical in other fields.
    - There can't be a cycle in the table_links e.g. A -> B -> C -> A is not allowed.
    - Each table needs to have N+1 identifier columns (id, patient_id, visit_id, etc.), where N is
      the  number of direct predecessors of each table. A direct predecessor is a table that has a
      direct link (depth=1) between itself and another table.

      For example, if a table has 2 direct predecessors, it needs to have 3 identifier columns.

      Examples:
        * A -> B <- C , A and C need to have 1 identifier column, and B needs to have 3 identifier
            columns.
        * A -> B -> C <- D , A and D need to have 1 identifier column, B needs to have 2, and
         C needs to only have 3 and not 4, because C only has 2 direct predecessors (B and D).
         There is not a direct link from C to A, so it is not a direct predecessor.
    - Each table can only be linked to the same variable once. This means that if A -> B <- C ,
      A and C need to link to two different columns in B.
    - Every table can only reference another table once. This means that A -> B <- A is
      not possible.
    - Top level tables need to be individual_level tables. For example, A -> B <- C , A and C need
      to be individual_level tables.
    """

    model_config = ConfigDict(
        extra="allow",
    )
    table_links: Annotated[
        List[TableLink],
        Field(
            description="Description of each relational link between tables.",
            min_length=1,
            title="Table Links",
        ),
    ]
    table_parameters: Annotated[
        List[
            Union[
                BaseAvatarizationParameters, BaseAvatarizationWithTimeSeriesParameters
            ]
        ],
        Field(
            description="Avatarization parameters for each table. ",
            min_length=1,
            title="Table Parameters",
        ),
    ]
    seed: Annotated[
        Optional[int],
        Field(
            description="Seed used to generate the random numbers.If not provided, a random seed will be generated.",
            title="Seed",
        ),
    ] = None


class AvatarizationMultiTableResult(BaseModel):
    datasets: Annotated[List[AvatarizationResultPerDataset], Field(title="Datasets")]


class AvatarizationWithTimeSeriesJobCreate(BaseModel):
    parameters: AvatarizationWithTimeSeriesParameters


class PrivacyMetricsGeolocationJobCreate(BaseModel):
    parameters: PrivacyMetricsGeolocationParameters


class PrivacyMetricsMultiTableJobCreate(BaseModel):
    parameters: PrivacyMetricsMultiTableParameters


class AdviceJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[Advice] = None
    parameters: AdviceParameters
    current_progress: Optional[JobProgress] = None


class AvatarizationMultiTableJob(BaseModel):
    id: Annotated[UUID, Field(title="Id")]
    kind: JobKind
    created_at: Annotated[datetime, Field(title="Created At")]
    status: JobStatus
    error_message: Annotated[Optional[str], Field(title="Error Message")] = None
    traceback: Annotated[Optional[str], Field(title="Traceback")] = None
    result: Optional[AvatarizationMultiTableResult] = None
    parameters: AvatarizationMultiTableParameters
    current_progress: Optional[JobProgress] = None


class AvatarizationMultiTableJobCreate(BaseModel):
    parameters: AvatarizationMultiTableParameters


@runtime_checkable
class Processor(Protocol):
    def preprocess(self, df: pd.DataFrame) -> pd.DataFrame: ...

    def postprocess(self, source: pd.DataFrame, dest: pd.DataFrame) -> pd.DataFrame: ...


class AvatarizationPipelineCreate(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    avatarization_job_create: AvatarizationJobCreate
    processors: List[Processor] = []
    df: pd.DataFrame


class AvatarizationPipelineResult(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    privacy_metrics: PrivacyMetrics
    signal_metrics: SignalMetrics
    post_processed_avatars: pd.DataFrame
    avatarization_job_id: UUID
    signal_job_id: UUID
    privacy_job_id: UUID
