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.
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 documentconstretrievedDocument=awaitsdk.data.documents.findById('blood-pressure-measurement',task.data.documentId);/* Analyze the document */constdiagnosis=awaitanalyzeDocument({ sdk, document: retrievedDocument});//Fetch the info of the user who created this documentconstuser=awaitsdk.users.findById(retrievedDocument.creatorId);//Create the PDFawaitcreatePDF({sdk, user, document: retrievedDocument, diagnosis });}exports.handler=async (task) => {constsdk=awaitgetSDK();awaitexports.doTask({sdk, task});};
asyncfunctioncreatePDF({sdk, user, document, diagnosis}) {// Find the pdf templateconstpdfTemplate=awaitsdk.templates.findByName("pdf-analysis");// Generate the PDF with the templateconstpdf=awaitsdk.templates.resolveAsPdf(pdfTemplate.id, {"language":"NL","time_zone":"Europe/Brussels","content": {"first_name":user.first_name,"category": diagnosis,"diastolic":document.data.diastolic,"systolic":document.data.systolic,"date":newDate(document.data.timestamp).toDateString(), } });// Find the id of the transition, needed for transitioning the documentconstschema=awaitsdk.data.schemas.findByName('blood-pressure-measurement');consttransition=schema.transitions.find(transition =>transition.name ==="add-report");// Upload the pdf to the file serviceconstfileResult=awaitsdk.files.create(`measurement-${document.id}`, pdf);// Transition the document to analyzedawaitsdk.data.documents.transition('blood-pressure-measurement',document.id,// Report property is added to the data to store the file service token { id:transition.id, data: { report:fileResult.tokens[0].token }} );}module.exports= { createPDF}
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:
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: