Translate line items description with external HTTP service API

You might be getting line items with item descriptions in many languages. However, at the end you need to link the products based on the English descriptions to your own product database. This function can enable you to translate descriptions of the items in just a single click.

This code example assumes you have the following field IDs in your extraction schema:

  • "item_description" for capturing the description column in line items
  • "localize_button" for having a button that user can click on and translate the line items to English

Moreover, the serverless function should be configured to get notifications on "user_update" action and the function should be assigned to some queue.

Copy the example below and paste it to your schema in order to create a button:

{
        "category": "datapoint",
        "id": "localize_button",
        "label": "Localize button",
        "type": "button"
}

Afterwards, create a Microsoft Azure account and setup an access to Translator API service.

Copy the authentication key and replace it in the function code below.

// This serverless function example can be used for annotation_content events
// (e.g. user_update action). annotation_content events provide annotation
// content tree as the input.
//
// The function below shows how to use an external HTTP service API to translate
// the description of line item from any language to english.

// Using https module for HTTP requests
const https = require('https');

// --- ROSSUM HOOK REQUEST HANDLER ---

// The rossum_hook_request_handler is an obligatory main function that accepts
// input and produces output of the rossum serverless function hook. Currently,
// the only available programming language is Javascript executed on Node.js 12 environment.
// @param {Object} annotation - see https://api.elis.rossum.ai/docs/#annotation-content-event-data-format
// @returns {Object} - the messages and operations that update the annotation content

exports.rossum_hook_request_handler = async ({
  annotation: {
    content
  },
  updated_datapoints
}) => {
  try {
    
    // User clicked or updated a specific datapoint (f.e. clicked on a button)
    const [updatedDatapoint] = findByDatapointId(
      content,
      updated_datapoints[0],
    );

    let messages = [];
    let operations = [];
    let dataString = '';
    
    
    if (updatedDatapoint && updatedDatapoint.schema_id == 'localize_button') {

      // Get all item_description datapoints
      let itemDescriptions = findBySchemaId(content, "item_description");

      let textsToTranslate = [];

      for (var i = 0; i < itemDescriptions.length; i++) {

        // Prepare translation input for the HTTPS request
        if (itemDescriptions[i].content.value != null) {

          textsToTranslate.push({
            "Text": itemDescriptions[i].content.value
          });

        }

      }

      // Create header for the HTTP request
      const options = {
        hostname: 'api.cognitive.microsofttranslator.com',
        path: '/translate?api-version=3.0&to=en',
        method: 'POST',
        headers: {
          'Ocp-Apim-Subscription-Key': '...authentication key...',
          'Ocp-Apim-Subscription-Region': 'westeurope',
          'Content-Type': 'application/json; charset=UTF-8',
        },
      };

      // Define how to process response
      const response = await new Promise((resolve, reject) => {
        const req = https.request(options, function(res) {
          res.on('data', chunk => {
            dataString += chunk;
          });
          res.on('end', () => {
            resolve({
              statusCode: 200,
              body: dataString
            });
          });
        });

        req.on('error', (e) => {
          reject({
            statusCode: 500,
            body: 'Something went wrong!'
          });
        });

        // Fill request with payload
        req.write(JSON.stringify(textsToTranslate))
        
        // Execute request
        req.end()
      });

      // Parse response
      translatedChunks = JSON.parse(dataString);

      // Update existing line item descriptions with translations
      for (var j = 0; j < translatedChunks.length; j++) {

        const translatedChunk = translatedChunks[j]["translations"][0]["text"];

        operations.push(createReplaceOperation(itemDescriptions[j], translatedChunk));

      }

    }

    return {
      messages,
      operations,
    };


  } catch (e) {
    const messages = [createMessage('error', 'Serverless Function: ' + e.message)];
    return {
      messages
    };
  }
};

// --- HELPER FUNCTIONS ---

// Return datapoints matching a schema id.
// @param {Object} content - the annotation content tree (see https://api.elis.rossum.ai/docs/#annotation-data)
// @param {string} schemaId - the field's ID as defined in the extraction schema(see https://api.elis.rossum.ai/docs/#document-schema)
// @returns {Array} - the list of datapoints matching the schema ID

const findBySchemaId = (content, schemaId) =>
  content.reduce(
    (results, dp) =>
    dp.schema_id === schemaId ?
    [...results, dp] :
    dp.children ?
    [...results, ...findBySchemaId(dp.children, schemaId)] :
    results,
    [],
  );

// Create a message which will be shown to the user
// @param {number} datapointId - the id of the datapoint where the message will appear (null for "global" messages).
// @param {String} messageType - the type of the message, any of {info|warning|error}. Errors prevent confirmation in the UI.
// @param {String} messageContent - the message shown to the user
// @returns {Object} - the JSON message definition (see https://api.elis.rossum.ai/docs/#annotation-content-event-response-format)

const createMessage = (type, content, datapointId = null) => ({
  content: content,
  type: type,
  id: datapointId,
});

// Replace the value of the datapoint with a new value.
// @param {Object} datapoint - the content of the datapoint
// @param {string} - the new value of the datapoint
// @return {Object} - the JSON replace operation definition (see https://api.elis.rossum.ai/docs/#annotation-content-event-response-format)

const createReplaceOperation = (datapoint, newValue) => ({
  op: 'replace',
  id: datapoint.id,
  value: {
    content: {
      value: newValue,
    },
  },
});

// Return a datapoint by its ID.
// @param {Object} content - the annotation content tree (see https://api.elis.rossum.ai/docs/#annotation-data)
// @param {string} datapointId - the datapoints ID. (https://api.elis.rossum.ai/docs/#common-attributes)
// @returns {Array} - datapoints matched by its ID (should return only one datapoint)

const findByDatapointId = (content, datapointId) =>
  content.reduce(
    (results, dp) =>
    dp.id === datapointId ? [...results, dp] :
    dp.children ? [...results, ...findByDatapointId(dp.children, datapointId)] :
    results,
    [],
  );

🚧

Enabling access to Internet for serverless function

Make sure the internet access was enabled on your functions. Contact us at [email protected] or read about the limitations.