Managing Reports + PDFs

Report Jobs

The report job endpoint allows for:

  1. creating profiles
  2. uploading genome files
  3. generating reports
  4. generating pdfs

We have two endpoints, one for existing profiles, another for new profiles.

In a single request, you can do any or all of these actions. You can control the desired end state through the desired_status field.

Valid options for desired_status are:

  1. profile_upserted (For creating new profiles only, not supported for existing profile endpoint)
  2. file_processed
  3. report_generated
  4. pdf_generated

Intent is inferred via the request payload.

Examples:

Create Profile, upload file, generate report + pdf

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/

{
  "desired_status": "pdf_generated",
  "report_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "profile_data": {
    "name": "John Doe",
    "email": "[email protected]",
    "ethnicity": "East Asian",
    "sex": "Male",
    "birth_year": 2000
  },
  "pdf_config": {
    "recommendation_type": "simple",
    "lab_results_type": "all"
  },
  "file_url": "https://my-bucket-name.s3.amazonaws.com/my-file-name.txt"
}

Upload file for existing profile

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/1ca85f64-5717-4562-b3fc-2c963f66afa6/

{
  "desired_status": "file_processed",
  "file_url": "https://my-bucket-name.s3.amazonaws.com/my-file-name.txt"
}

Generate report for existing profile

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/1ca85f64-5717-4562-b3fc-2c963f66afa6/

{
  "desired_status": "report_generated",
  "report_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
}

Uploading Files

One - Provide a file_url

We will attempt to download the file from the url. Timeout is 20 seconds.

Two - Use presigned post

First, call /report-job/upload-file. This will return a presigned post url to s3 with authentication fields and a file_id. Note - this is not your genome file id. It is a temporary id to identify the uploaded file. A genome file is only created on successful POST to /report-job/ with said file_id.

After completing the upload, make a POST request to /report-job/ including the provided file_id. File_ids will expire after a couple hours.

import requests

headers = {"Authorization": "Token XYZ"}
base_url = "https://qa-b2b.selfdecode.com/service/b2b-integrations/report-job/"

# get presgined post url
response = requests.post(base_url + "upload-file/", headers=headers, timeout=30).json()
url, fields, file_id = response["url"], response["fields"], response["file_id"]
file_path = "genome_file.txt"

# upload file to s3
with open(file_path, "rb") as f:
    files = {"file": ("genome_file.txt", f)}
    requests.post(url, data=fields, files=files, timeout=60)

# generate report job with file_id
base_payload = {
    "profile_data": {
        "name": "John",
        "sex": "Male",
        "birth_year": 1992,
        "ethnicity": "East Asian",
    },
    "desired_status": "pdf_generated",
    "file_id": file_id,
    "report_id": "ff4f5b07-89fe-4a02-a151-60ff0de8b721",
}
response = requests.post(base_url, headers=headers, json=base_payload)

Fetching Report Jobs

List

/report-job/ supports listing by parameters: report_id, profile_id, status

Retrieve

Individual report jobs can be retrieved by their id at report-job/:pk.

Retrieve by profile_id, report_id combination

The latest matching report job can be retrieved via report-job/profile/:profile-id/report/:report-id.

PDFs

Downloading

Once a report job's pdf has finished generated, its response payload will contain a pdf_url with an s3 presigned get url. The pdf can be downloaded from this link.

Configuration

PDFs support all of the same configurations as before. See PDF Configurations for details.

In addition, the pdf_file_name can be controlled via the initial post request.
e.g.

{
  "desired_status": "pdf_generated",
  "profile_id": "1ca85f64-5717-4562-b3fc-2c963f66afa6",
  "report_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "pdf_file_name": "my-report.pdf"
}

Expiry

By default pdfs expire after 24 hours and need to be recreated via another report-job.

If you intend to use the same pdf many times, you can request for permanent storage by passing "is_permanent": True in the pdf_config of the create payload.

The pdf_url will still expire after 24 hours, but can refreshed by re-retrieving the report job. Simply listing the report job will not refresh the pdf_url.

Other PDFs

Here, we're only concerned with PDFs of DNA wellness reports. To generate PDFs for other pages, like for your lab results, refer to the Managing Other PDFs section.

Bulk Creating

For mass report/pdf generation there are two bulk-create endpoints, one for existing profiles and one for new profiles. Both accepts a payload identical to the standard /report-job payload, except instead of a report_id field, a report_ids field is used. This field expects a list of report_ids which must be non-empty and contain a maximum of 250 report IDs.

Bulk Creating for an existing profile

For mass report/pdf generation for an existing profile, the /report-job/profile/{profile_id}/bulk-create/ endpoint can be used.

Example:

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/1ca85f64-5717-4562-b3fc-2c963f66afa6/bulk-create/

{
  "desired_status": "pdf_generated",
  "report_ids": ["3fa85f64-5717-4562-b3fc-2c963f66afa6","9ca85f64-5717-4562-b3fc-2c963f66afa6"],
}

Bulk Creating for a new profile

For mass report/pdf generation a new profile, the /report-job/profile/bulk-create/ endpoint can be used. It supports profile_data along with a list of report_ids.

Example:

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/bulk-create/

{
  "desired_status": "pdf_generated",
  "profile_data": {
    "sex": "Male",
    "name": "Profile Name",
    "email": "[email protected]",
    "ethnicity": "Other",
    "birth_year": 2000
  },
  "report_ids": ["3fa85f64-5717-4562-b3fc-2c963f66afa6","9ca85f64-5717-4562-b3fc-2c963f66afa6"],
}

Generate All

To generate all reports there are two endpoint, one to generate all reports for an existing profile, and another for a new profile.

Generate All for an existing profile

To generate all reports for an existing profile, the /report-job/profile/{profile_id}/generate-all/endpoint can be used.

Example:

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/1ca85f64-5717-4562-b3fc-2c963f66afa6/generate-all/

{
  "desired_status": "pdf_generated"
}

Generate All for a new profile

To generate all reports for a new profile, the /report-job/profile/generate-all/endpoint can be used.

Example:

POST https://selfdecode.com/service/b2b-integrations/report-job/profile/generate-all/

{
  "desired_status": "pdf_generated",
  "profile_data": {
    "sex": "Male",
    "name": "Profile Name",
    "email": "[email protected]",
    "ethnicity": "Other",
    "birth_year": 2000
  }
}

Webhooks

Please check our webhooks guide for detailed instructions on using webhooks.

Building Report Detail Page

Composed Profile Report

This single endpoint contains all information required to display most reports (for summary reports see here)

High Level Breakdown

  • Report - generic info/text for given report
  • Profile Trait - report outcome
    • Trait - generic info/text for given trait
  • Profile Report Recommendations - personalized recommendations
    • Report Recommendation - generic info/text fo given trait
    • Satisfied Conditions - info for report rec conditions for given profile satisfies

ProfileTrait

Each profile report is associated with a particular outcome for that report. This outcome is encapsulated in the profile-trait response.

Here is an example profile-trait response (text has been abbreviated):

{
    // meta data
    "id": "7048f855-e266-4f86-bf8c-2954c4e3af14",
    "profile_id": "adfbc357-d2d2-462c-a417-22474fddd646",
    "profile_report_id": "178711fb-1aa3-4498-a5c6-e60d49b57f3c",

    // outcome fields
    "outcome_id": "378711fb-1aa3-4498-a5c6-e60d49b57f3c",

    // personalized
    "trait_score_text": "Likely higher lactate levels",
    "personalized_text": null,
    "personalized_recommendation_text": null,
    "number_of_snp": 353451,
    "genetic_variants": [],

    // trait info (non-personalized)
    "trait": {
        // meta data
        "id": "67d64155-1944-4bf3-b7df-b0fbeed27984",
        "slug": "39e3b79e-4177-4b88-acbb-abea3f506d18",
        "report_id": "2735f15d-3124-4d9c-8ff1-9262b44292f6",

        // text fields
        "name": "Lactate",
        "summary": "",
        "text": "...",
        "text_header": "Genetics of Lactate Levels",
        "genetic_intro": "",
        "disclaimer": "...",

        // feedback
        "feedback_question": "Have you ever been told your lactate levels are high?",
        "feedback_choices": [],

        // gauge fields
        "gauge_title": "",
        "gauge_subtitle": "",
        "gauge_type": "emoji",
        "gauge_range_type": "segmented",
        "gauge_coloring": "block",
        "gauge_direction": null,
        "gauge_num_of_divisions": 2,
        "gauge_labels": [
            {
                "id": "195a3071-25b4-41da-9d8d-96f10c2cf07b",
                "label_text": "Higher Levels",
                "label_sub_text": "",
                "label_color": "red",
                "ratio": null,
                "display_order": 2,
                "outcome_id": "375a3071-25b4-41da-9d8d-96f10c2cf07b",
            },
            {
                "id": "ae3abb67-165b-48d0-a957-801f58ce8d32",
                "label_text": "Typical Levels",
                "label_sub_text": "",
                "label_color": "yellow",
                "ratio": null,
                "display_order": 1,
                "outcome_id": "378711fb-1aa3-4498-a5c6-e60d49b57f3c",
            }
        ]
    },

}

Report Outcomes

All reports regardless of underlying implementation capture the report outcome in this outcome_id field. Use this to determine the matching gauge label.

The exception to this rule are aggregate reports (e.g. Fatigue). These are simple aggregates of many sub reports and have no outcome.

Report Gauges

There are three gauge types. The gauge type is exposed via trait.gauge_type field:

  1. emoji
  2. needle
  3. pie (see here)

These are further customized through the gauge_labels (where matching range, color and label text can be configured), and gauge_coloring, gauge_direction and gauge_range_type fields.

Example payload:

{
  // ...
  "trait": {
    // ...
    "gauge_title": "", // pie chart only
    "gauge_subtitle": "", // pie chart only

    // emoji, needle or pie
    "gauge_type": "emoji",

    // segmented, continuous or null
    // determines whether needle should point at exact value
    // or just centre of the segment
    "gauge_range_type": "segmented",

    // block or gradient
    // whether gauge colors should be gradiented or block colors
    "gauge_coloring": "block",

    // clockwise, counterclockwise (null => clockwise)
    "gauge_direction": null,

    // number of segments (null => 3)
    "gauge_num_of_divisions": 2,

    "gauge_labels": [
      // ...
      {
        "id": "195a3071-25b4-41da-9d8d-96f10c2cf07b",
        "label_color": "red",
        "label_text": "Higher Levels",
        "label_sub_text": "", //pie chart only
        "ratio": null, //pie chart only
        "display_order": 2,

        // outcome match
        "outcome_id": "195a3071-25b4-41da-9d8d-96f10c2cf07b",
      },
    ]
  }
}
Gauge Defaults

Needle gauges require all gauge fields and gauge labels to be inputted.

Emoji gauges accept defaults when the relevant fields are null. Default ranges are: 0.0-0.2 and 0.2-1.0 for 2 segmenet gauges, and 0.0-0.2, 0.2-0.8 and 0.8-1.0 for 3 segement gauges (or gauge_num_of_divisions is null).

ProfileReportRecommendation

The profile_report_recommendation contains a presorted list of recommendations from most to least useful (descending impact). Recommendations that are judged irrelevant for the user (based on their specific results) will be marked as is_relevant=False and we do not display them on our frontend.

The payload contains a satisfied_conditions field which returns an array of matched report recommendation conditions. Each report recommendation condition contains some conditionial_text which gives additional advice or context for the specific user.

Note that there are several possible types of recommendation trait condition (snp, trait score, case and scenario). Some fields may vary depending on the type in question.

Example Payload (text has been abbreviated):

[
  {
    // meta data
    "id": "a8c7b7c7-aead-41d6-afc4-004fd3b3cf71",
    "profile_report_id": "44b3b2f6-f56b-4b40-9676-04ccef704f24",
    "is_relevant": true, // we hide non-relevant

    // personalized outcome
    "impact": 0.4, // impact of the recommendation (previously effect)
    "evidence": 0.6, // evidence for the recommendation
     "satisfied_conditions": [{
      "priority": 1, // display order
      "conditional_text": "...",
      "rsid": "rs123"
      "snp_trigger_evidence": 0.5
    }],

    "report_recommendation": {
      // meta data
      "id": "9d71c416-b416-4494-b041-5eac7e65470d",
      "recommendation_id": "exercise",
      "report_id": "0dad0d4c-ba5d-4409-a8e8-f7832540b84b",
      "connection": "positive", // whether or not helps with report
      "recommendation_type": "lifestyle",

      // related marker
      // e.g. vitamin D recommendation
      // might point to marker for vitamin D
      "related_marker_id": null,

      // text or UI
      "name": "Maintain a Healthy Weight",
      "generic_text": "...",
      "sentence": "Try to maintain a healthy weight.",
      "illustration": "https://selfdecode.com/cdn/media/reports/Maintain_a_Healthy_Weight.svg",

      // fields used in combination with care plan
      "is_avoidance_recommendation": false,
      "is_treatment": false,
      "is_prevention": false,
      "default_frequency_value": null,
      "default_frequency_unit": null,
      "default_duration_value": null,
      "default_duration_unit": null,
      "conflicting_slugs": []
    }
  }
]

Summary Reports

There are two types of summary reports: aggregate and combined.

Aggregate reports collect all the results and recommendations from reports on a single topic, e.g. sleep. They provide a single, comprehensive point of access for diving into a particular health topic.

Combined reports are an extension of aggregate reports. In addition to categorising all information on a topic into sections, they provide further outcome advice depending on the specific combination of sub report results. Currently, there are only two combined reports: Diet and Nutrition and Fitness.

Summary Report Generation

Summary reports are generated using the same flow as standard reports. The generation pipeline will guarantee that all sub reports are generated before marking the parent summary report as completed. In the case of combined reports, the matched case will also be assigned.

Note - generating a summary report will generate all sub reports associated with that summary report.

Aggregate Reports

Aggregate and Combined reports are composed of report sections. Each report section in turn points to a number of sub reports. This information is located in the report_sections field of a report payload

Example Payload

{
  //...
  "report_sections": [
    {
      // meta fields
      "id": "6f929bc8-142d-4661-8085-9a7db20cd8a3",
      "slug": "sleep-disturbances",
      "report_id": "58f018e0-32e5-4c91-ba2a-ac161205094d",

      //text fields
      "title": "Sleep Disturbances",
      "text": "...",

      // ui fields
      "tooltip": "Sleep Disturbances",
      "color": "#2b7eef",
      "order": 4,
      "illustration": "...",
      "icon": "...",

      "sub_reports": [
        {
          "parent_report_id": "58f018e0-32e5-4c91-ba2a-ac161205094d",
          "child_report_id": "39bd8e00-5c1b-4280-9618-81baa829a740",
          "intro": "..."
        }
      ]
    }
  ]
}

Aggregate Reports have no associated outcome and their profile_trait.outcome_id will be null.

Aggregate Recommendations

Summary reports have no recommendations of their own. Instead they aggregate the recommendations of their sub reports. To do this, they use two endpoints:

  1. /profile-report-recommendation/?profile_report_id=x,y,z to fetch the related profile report recommendations (where x,y,z are profile report ids of sub reports)
  2. /recommendation-id/?profile_id=q&sort=-impact&report_id=a,b,c for sort order (where a,b,c are report ids of sub reports)

/profile-report-recommendation/ uses the same format as composed profile report (see ProfileReportRecommendation)

/recommendation-id/ provides a array of objects with an id field
e.g. [{"id":"exercise}]. These correspond to the recommendation_id field within the report_recommendation

Combined Reports

Combined Reports are almost identical to aggregate reports. They are organized into report sections that point to relevant sub reports. However, combined reports also have an outcome associated with the combination of certain sub report results (typically those included in the first report section). This outcome is displayed through a pie chart.

Combined Report Outcomes

A combined report will have a populated outcome_id and may have an additional_info field associated with its profile_trait.

Example:

{
  "outcome_id":  "31185ea0-50aa-4cc7-b2cb-4c5228715e78"
  "additional_info": {
    "results": ["basketball", "cycling", "surfing", "rugby", "wrestling"]
  }
}
Pie Chart

Pie Chart gauges are only used with combined model_type traits.

Example pie chart gauge label

{
    "id": "7d185ea0-50aa-4cc7-b2cb-4c5228715e78",

    "label_text": "Power",
    "label_sub_text": "",
    "label_color": "pie orange",

    "ratio": 40,
    "display_order": 1,
    "outcome_id": "43185ea0-50aa-4cc7-b2cb-4c5228715e78"
}

Gauge labels should be filtered by the outcome_id. This should give you the same number of labels as expected slices in the pie chart. The ratio field is then used to distribute the pie among the slices.

Debugging

Timeouts

If you face timeouts, try adding "Accept-Encoding": "gzip, deflate, br" to your request headers so that they can benefit from compression.