Workflow 2: Create & store a PDF report

Now that the measurement is analyzed, let's create a simple PDF report which is stored in the file service & can be downloaded by your users.

Basic concepts

In the previous workflow, we've used the data service to save measurement data to Extra Horizon. The data service is designed for storing structured data. However, not all data can be written in a structured manner.

In this section, we'll leverage the file service to handle unstructured data. While the data-service is designed for storing structured data, the file service is ideal for managing unstructured data. In this section, you'll learn how to create a PDF document from a template and seamlessly interact with the file service using the SDK. You can learn more about the file service here.

Every file stored in the file service receives a unique token. These tokens are essential for retrieving files from the file service. Within the function, we will add the token for the PDF to the blood pressure measurement. This way, users can effortlessly access their reports whenever needed.

Create a PDF template

Extra horizon has a separate service for managing templates. This is typically used for creating mails or PDF's, but it's not necessarily limited to that. Because they are managed by a service, they can be updated independently of the tasks that are using them. For more information about the template service, see here.

Under 2-workflows/templates/pdf-analysis you'll find the template that we're going to use.

{
  "description": "Template used to generate a pdf-analysis",
  "name": "pdf-analysis",
    "schema": {
    "type": "object",
    "fields": {
      "first_name": {
        "type": "string"
      },
      "category": {
        "type": "string"
      },
      "diastolic": {
        "type": "number"
      },
      "systolic": {
        "type": "number"
      },
      "date": {
        "type": "string"
      }
    }
  },
  "fields": {
  }
}

This template consists of 2 files: an HTML file and a JSON file.

The JSON file defines the structure of the template variables, while the HTML file is the template where those variables are used.

The reason why it is split in 2 files, is because mixing HTML and JSON in 1 file doesn't make for a pleasant developer experience.

When uploading the template, the CLI will read the HTML file, add it as a body property in the fields object in the JSON file and upload the result.

Update schema

We are going to update our schema a bit to accomodate this new feature:

  • An additional property to store a file token.

  • A new status to indicate that the report is available, report-available

  • Since we added a new status, we also need a new transition. We only allow transitions to the report-availabe state coming from the analyzed state.

As mentioned in the introduction, a file token is something you get back when you store a new file in the file-service. It's your personal key to access the file. Make sure to store this token in a secure way, because any user with this token is able to download the file.

{
  "name": "blood-pressure-measurement",
  "description": "Blood pressure measurement",
  "statuses": {
    "created": {},
    "analyzing": {},
    "analyzed": {},
    "report-available": {}
  },
  "creationTransition": { ... },
  "transitions": [
    ...
    {
      "name": "add-report",
      "type": "manual",
      "toStatus": "report-available",
      "fromStatuses": [ "analyzed" ],
        "conditions": [
          {
            "type": "input",
            "configuration": {
              "type": "object",
              "properties": {
                "report": {
                  "type": "string"
                }
              },
              "required": ["report"]
            }
          }
        ]
    }
  ],
  "properties": {
    ...
    "report": {
      "type": "string",
      "description": "File-token of the report"
    }
  }
}

Update your task to create a PDF

Next we need to update the task to create the PDF, upload it to the file service and store the file token in the document.

const { getSDK } = require("./sdk");
const { analyzeDocument } = require("./diagnose");
const { createPDF } = require("./create-pdf");

exports.doTask = async ({sdk, task}) => {
  //Read the blood pressure document
  const retrievedDocument= await sdk.data.documents.findById('blood-pressure-measurement', task.data.documentId);

  /* Analyze the document */
  const diagnosis = await analyzeDocument({ sdk, document: retrievedDocument});

  //Fetch the info of the user who created this document
  const user = await sdk.users.findById(retrievedDocument.creatorId);

  //Create the PDF
  await createPDF({sdk, user, document: retrievedDocument, diagnosis });
}

exports.handler = async (task) => {
  const sdk = await getSDK();

  await exports.doTask({sdk, task});
};

Feel free to examine the code to see how we archieved this using the SDK.

Deploy everything

First we need to build the task. In 2-workflows do

npm run build-flow-2

And then to sync everything:

npx exh sync

Test the updated task

Use the scripts in examples to test your task as follows:

➞  node create-measurement.js                                                                                                    
Enter systolic value: 10
Enter diastolic value: 20
🎉 Created a new measurement document with id 6568537e5a8b65a7c7e3de77

➞  node get-measurement.js                                                                                                       
Enter document ID to retrieve: 6568537e5a8b65a7c7e3de77
Retrieved document 6568537e5a8b65a7c7e3de77
{
    "id": "6568537e5a8b65a7c7e3de77",
    "groupIds": [],
    "userIds": [
        "6310a263cff47e0008fb2221"
    ],
    "creatorId": "6310a263cff47e0008fb2221",
    "status": "report-available",
    "statusChangedTimestamp": "2023-11-30T09:19:07.591Z",
    "data": {
        "systolic": 108,
        "diastolic": 83,
        "timestamp": "2023-11-30T09:18:54.756Z",
        "category": "normal",
        "report": "6568538b657f242ca661d0a4-21254617-4848-4cb0-a83b-ee93ec9ef45d"
    },
    "updateTimestamp": "2023-11-30T09:19:07.598Z",
    "creationTimestamp": "2023-11-30T09:18:54.872Z"
}
➞  node get-file.js                                                                                                                 
Enter file token: 6568538b657f242ca661d0a4-21254617-4848-4cb0-a83b-ee93ec9ef45d
Retrieved file & stored as report.pdf

Go ahead and open the PDF file to see the report!

Test your task locally

Important: when testing locally, make sure that the analyze-blood-pressure task is not running in the Extra Horizon backend. Otherwise any created measurement will automatically be process by that task. You can easily remove the task using the CLI:

npx exh tasks delete --name=analyze-blood-pressure

In examples there's a test-local-task-flow-2.js script to run the task locally.

Last updated