Creating Predictions

Guide for creating and ingesting predictions, including job-based workflows.

This tutorial explains how to use the predictions endpoints to create, process, and ingest prediction data using a job-based workflow.

## Overview

The prediction ingestion process typically involves: 1. Retrieving Jobs: Fetching active jobs that define what predictions need to be made. 2. Processing: Fetching external data (e.g., weather forecasts) based on the job configuration. 3. Creating Predictions: Posting the calculated predictions back to the API.

> Note: The job-based workflow (Retrieving Jobs) described below is optional. It is designed to make your prediction scripts configurable via the UI (e.g., managing which locations to forecast for). You can also directly POST predictions to the API without fetching jobs if you prefer a simpler, hardcoded integration.

## 1. Retrieving Jobs

To build a configurable worker that knows what to predict, you first fetch the list of "Jobs" assigned to your service version.

### `GET /api/v1/jobs/service/{service_id}`

Retrieves all jobs associated with a specific service version.

Authentication: Required.

Parameters: - `service_id` (UUID string, in path): The ID of the service version you are running as.

Example Request:

bash
curl "http://localhost:8000/api/v1/jobs/service/550e8400-e29b-41d4-a716-446655440000"   -H "Authorization: Bearer <your_token>"

Response: Returns an array of Job objects containing: - `id`: The Job UUID. - `jobrolebindings`: Defines inputs (e.g., location coordinates, input sources). - `job_targets`: Defines the output roles (metrics) expected for this job (e.g., "temperature", "humidity").

## 2. Creating Predictions

Once you have your data, you submit it to the API.

### `POST /api/v1/predictions`

This endpoint is used to ingest new prediction data.

Request Body:

A JSON object (Job-Based) OR a JSON Array (Direct).

Job-Based Payload (Recommended for Workers):

json
{
  "job_id": "uuid...",
  "service_id": "uuid...",
  "run_at": "2023-10-27T10:00:00Z",
  "predictions": [
    {
      "datetime_measure": "2023-10-27T10:00:00Z",
      "targets": [
        {
          "target_id": "uuid-of-target",
          "value_num": 25.4
        }
      ]
    }
  ]
}

Direct Payload (Simple):

json
[
  {
    "metric": "temperature",
    "source": "my-source",
    "location_id": "uuid...",
    "datetime_measure": "2023-10-27T10:00:00Z",
    "value": 25.4
  }
]

## Step-by-Step Example: Rust Worker

Below is a walkthrough of how a production-grade worker (written in Rust) typically implements this flow. You can adapt this logic to any language (Python, Node.js, etc.).

### Step 1: Login

Authenticate with the API to get a token.

rust
// POST /serviceLogin with service_id and password
let login_body = serde_json::json!({
    "service_id": service_id,
    "password": service_password
});
// ... Receive token ...

### Step 2: Fetch Jobs

Get the list of active jobs to see what work needs to be done.

rust
// GET /api/v1/jobs/service/{service_id}
let jobs_url = format!("{}/api/v1/jobs/service/{}", base_url, service_id);
let jobs: Vec<Job> = client.get(&jobs_url).send().await?.json().await?;

### Step 3: Process Each Job

Loop through the jobs. For each job, look at its bindings to find input parameters (like Location).

rust
for job in jobs {
    // Find the location binding to know WHERE to forecast for
    let location = job.job_role_bindings.iter()
        .find(|b| b.input_type == "location")
        .unwrap()
        .location;

    println!("Processing job {} for location: {}", job.id, location.name);

    // Call external API (e.g. Weather Provider) using the location coordinates
    let weather_data = fetch_weather(location.latitude, location.longitude).await?;
}

### Step 4: Map Data to Targets

The tough part is mapping external data (e.g., "tempc") to the specific `targetid` requested by the Job.

rust
// Iterate through the job's 'targets' to see what metrics are required
for target in job.job_targets {
    // If the job asks for "temperature", look for that in your weather data
    if target.role == "temperature" {
        let value = weather_data.get_temperature();
        
        // Add to your prediction payload using the explicit target_id
        current_prediction.targets.push(PredictionTarget {
            target_id: target.target_id, // Important: Use the ID from the job
            value_num: Some(value),
            ..
        });
    }
}

### Step 5: Submit Predictions

Send the constructed payload back to the API.

rust
// POST /api/v1/predictions
let request = PredictionRequest {
    job_id: job.id,
    predictions: my_predictions
    // ...
};

client.post("/api/v1/predictions").json(&request).send().await?;

This pattern allows you to add new Locations or change required Metrics in the Dashboard without modifying or redeploying your code!