Hide Table of Contents
View DataReviewer - Update Result Lifecycle Status sample in sandbox
DataReviewer - Update Result Lifecycle Status


When a feature or row is submitted to the Reviewer workspace it becomes a Reviewer result. Data Reviewer provides a defined data quality control workflow that helps you review a result, correct any problems with the result, and then verify the fix to the result. Lifecycle status represents the state of a Reviewer result in the workflow. This sample updates Lifecycle Status of Reviewer results to Data Reviewer for Server using the updateLifecycleStatus function. This sample also demonstrates the use of ReviewerLifecycle class. This class has a function getLifecycleInfo which determines the next appropriate lifecycle status and phase that the result(s) will advance to. Select the Records per page, Session Name and Return Fields. The Reviewer Results will be displayed in a grid. To update Lifecycle status, select results from the grid and click on Update Status button. The live sample updates Lifecycle status to a hosted instance of Data Reviewer for Server.


<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Reviewer Results UpdateLifecycleStatus Sample</title>
<link rel="stylesheet" href="https://js.arcgis.com/3.20/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.20/dojox/grid/resources/claroGrid.css">
<script src="https://js.arcgis.com/3.20/"></script>
.reviewerForm {
    width: 530px;
    font-size: .7em;
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
.reviewerForm input, .reviewerForm textarea{
	padding: 5px;
	width: 371px;
	margin: 0px 0px 10px 0px;
	border: 2px solid #CCC;
	font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
.submitButton {
	float: right;
	padding: 5px;
	width: 150px;
	font-size: 1.1em;
	border: 2px solid #CCC;
	margin: 0px 0px 10px 0px;
	vertical-align: top;
	font-size: 1.0em;
	font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
.reviewerForm select{
	padding: 5px;
	width: 385px;
	margin: 0px 0px 10px 0px;
	border: 2px solid #CCC;
.reviewerForm label {
	float: left;
	text-align: right;
	margin-right: 15px;
	width: 130px;
	padding-top: 5px;
.button {
   float: right;
   padding: 4px;
   width: 100px;
   font-size: 0.8em;
   border: 1px solid #CCC;
.reviewerGrid {
   font-size: 0.75em;
   font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
//This should point to the Data Reviewer soe
var drsSoeUrl = "https://datareviewer.arcgisonline.com/arcgis/rest/services/Samples/reviewerWriteResults/MapServer/exts/DataReviewerServer";
//array of return fields
var returnFields = [];
//Records per page
var recordsPerPage;
  "dojox/grid/DataGrid", "dijit/Dialog",
], function(parser, dom, on, array, ItemFileReadStore, DataGrid,Dialog, MultiSelect,
 ReviewerResultsTask, GetResultsQueryParameters, ReviewerFilters, ReviewerLifecycle, registry) {
    // specify proxy for request with URL lengths > 2k characters
    esriConfig.defaults.io.proxyUrl = "/proxy/";
    var reviewerResultsTask,selectedItems;
    // create ReviewerResultsTask instance 
    var reviewerResultsTask = new ReviewerResultsTask(drsSoeUrl);
    //get list of return field names
    var fieldNames =  reviewerResultsTask.getResultsFieldNames();
    //Populate field names to be returned by Get Results operation
    var i=0;
    array.forEach(fieldNames, function(field) {
      //Removing mandatory fields from the list. These fields will be added in the datagrid manually
      if (field !== "recordId" && field !== "sessionId" && field !== "lifecycleStatus"){
        dom.byId("fieldNamesCombo")[i] = new Option(field,i);
    //Click event of previousButton button
    on(dom.byId("previousButton"), "click", loadPreviousPage);
    //Click event of nextButton button
    on(dom.byId("nextButton"), "click", loadNextPage);
    //Click event of getResultsButton button
    on(dom.byId("getResultsButton"), "click", populateDataGrid);
    //Click event of Update Status button
    on(dom.byId("displayDialogButton"), "click", displayStatusDialog);
    //Click event of Update button
    on(dom.byId("updateStatusButton"), "click", updateStatus);

    function getReviewerSessions(){
        reviewerResultsTask = new ReviewerResultsTask(drsSoeUrl);
        var deferred=reviewerResultsTask.getReviewerSessions();
        deferred.then(function(response) {
            var reviewerSessions = response.reviewerSessions;
            for (var i = 0; i < reviewerSessions.length; i++) {
                dom.byId("sessionIdCombo")[i+1] = new Option(reviewerSessions[i].toString(), reviewerSessions[i].sessionId);
          }, function(error) {
    function populateDataGrid() {
        // Get the number of records per page
        recordsPerPage = parseInt(dom.byId("RecordsPerPage").value);
        //Get Selected fields from the list box
        var selectItem = registry.byId('fieldNamesCombo').getSelected();
        if(isNaN(recordsPerPage) || recordsPerPage < 1){
           alert("Please fill out records per page.");
          alert("Records per page can not be greater than 1000.");
        if(selectItem.length < 1){
            alert("Please select return fields.");
        //Generate grid layout and returnFields based on fields selected in the list box
        var layout = [];
        returnFields = [];
        // adding mandatory fields for updating lifecycle status in the layout.
        layout.push({ field:'recordId',	name:'recordId' });
        layout.push({ field:'sessionId',name:'sessionId' });
        layout.push({ field:'lifecycleStatus',name:'lifecycleStatus', 'formatter':getFormattedString });
        //adding field in return fields list
        array.forEach(selectItem, function(option) {
            layout.push({ field:option.text,name:option.text });	
        //show the grid with a loading message and populate it
    function loadDataInGrid(moveType,layout) {
        showGrid(true, 'loading');
        // create ReviewerResultsTask instance 
        var queryParameters=GetQueryParameters(moveType);
        // call getResults to retrieve the results stored in the reviewer workspace.
        var reviewerFilters = new ReviewerFilters();
        reviewerFilters.addAttributeFilter("SESSIONID", dom.byId("sessionIdCombo").value);
        var deferred = reviewerResultsTask.getResults(queryParameters,reviewerFilters);            
        // we're using dojo deferred 'then' function to set callback and errback functions
        deferred.then(function(response) {
            // map attributes from features to items array to populate dataGrid
            var items = array.map(response.featureSet.features, function(feature) {
                return feature.attributes;
            // create a dojo ReadStore
            var store = new ItemFileReadStore({
              data : {
                 items : items
            // set the store in the DataGrid, this will populate the datagrid with the reviewer results
            //update current page number label
            if(moveType == "previous" && (parseInt(dom.byId('lblPageNumber').innerHTML) > 1)){
                dom.byId('lblPageNumber').innerHTML =  parseInt(dom.byId('lblPageNumber').innerHTML) - 1;
            else if(moveType == "next"){
                dom.byId('lblPageNumber').innerHTML =  parseInt(dom.byId('lblPageNumber').innerHTML) + 1;
        }, function(err) {
            if(moveType == "next" && (err.message.indexOf("No results returned for specified query:") === 0)){
                alert("You are already at the last page.");	
                alert("Error retrieving reviewer results: " + err.message);
    function getFormattedString(value, obj,column){
        if (value===null){
            return "";
            if (value===null){
                return null;
            return ReviewerLifecycle.toLifecycleStatusString(value);
        else if (column.field=="verificationDateUtc" || 
        column.field=="correctionDateUtc" || column.field=="reviewDateUtc"){
            var date = new Date(value);
            var localDate = new Date(date.toLocaleString());
            return localDate.toDateString();
    function GetQueryParameters(moveType){
        var queryParameters = new GetResultsQueryParameters();
        queryParameters.pageSize = recordsPerPage;
        if(moveType == "previous" && (parseInt(dom.byId('lblPageNumber').innerHTML) > 1)){
            queryParameters.pageNumber = parseInt(dom.byId('lblPageNumber').innerHTML) - 2;
        else if(moveType == "next"){
            queryParameters.pageNumber = parseInt(dom.byId('lblPageNumber').innerHTML);
        else if (moveType===""){
            queryParameters.pageNumber = parseInt(dom.byId('lblPageNumber').innerHTML) - 1;
            queryParameters.pageNumber = 0;
        queryParameters.returnFields = returnFields;
        return queryParameters;
    //load next page
    function loadNextPage() {
    //load previous page
    function loadPreviousPage(){
        if(parseInt(dom.byId('lblPageNumber').innerHTML) == 1){
            alert("You are already at the first page.");
            loadDataInGrid("previous", "");	
    //Displays the next lifecycle status that the selected results can move to in a dialog
    function displayStatusDialog(){
         selectedItems = reviewerResultsGrid.selection.getSelected();
         if (selectedItems.length>0){
            var lifecycleList = [];
            //get an array of unique lifecycle status values of selected records
            for (i = 0; i < selectedItems.length; i++){
                if (lifecycleList.indexOf(selectedItems[i].lifecycleStatus[0]) == -1){
           //get lifecycle information from the ReviewerLifecycle helper class
            var lifecycleInfo = ReviewerLifecycle.getLifecycleInfo(lifecycleList);
            if (lifecycleInfo !== null){
                // clear out any previous values
                array.forEach (lifecycleInfo.nextLifecycleStatus,function(status,i){
                    dom.byId("statusSelect")[i+1] = new Option(ReviewerLifecycle.toLifecycleStatusString(status), status);
                alert("The selected records cannot be updated to a common lifecycle status");
            var statusDialog = registry.byId("statusDialog");
            alert("Please select records to update");	
    //calls the update lifecycleStatus method from the ReviewerResults task. If selected results are updated successfully a message will be displayed
    function updateStatus(){
        var reviewerFilters = new ReviewerFilters();
        array.forEach(selectedItems, function(selectedItem){
            reviewerFilters.addAttributeFilter("RECORDID", selectedItem.recordId);
        var deferred=reviewerResultsTask.updateLifecycleStatus(dom.byId("sessionIdCombo").value,dom.byId("statusSelect").value,dom.byId("technicianName").value,reviewerFilters);
            //refresh datagrid
            array.forEach(response.featureEditResults, function(featureEditResult){
                if (featureEditResult.success===false){
                    alert("Error updating records from the selection");
            alert("Lifecycle Status updated");
        }, function(error){
    //handle grid visibility
    function showGrid(show,message) {
        reviewerResultsGrid.showMessage(message || "");
        dom.byId('gridDiv').style.visibility = show ? "visible" : "hidden";
<body class="claro">
    <h2 align="center">Update Lifecycle Status Sample</h2>
	<div style="width:100%; overflow-x: auto;" >
    <div style="padding: 0px 20px 0px 20px; float:left;">
        <div class="reviewerForm" >
            <label for="RecordsPerPage">Records per page:</label>
            <input type="number" id="RecordsPerPage" placeholder="valid values are from 1 to 1000"/>
            <label for="sessionIdCombo">Session Name:</label>
              <select id="sessionIdCombo" ><option value="">Select Session</option>
            <label for="fieldNamesCombo">Return fields:</label>
             <select class ="reviewerMultiSelect" id="fieldNamesCombo" data-dojo-type="dijit/form/MultiSelect"></select>
            <label for="getResultsButton"></label>
            <button id="getResultsButton" class="submitButton">Get Results</button>
    <div id = "gridDiv" style="width: 100%; overflow-x: auto;visibility: hidden" class="reviewerGrid">
        <button id="previousButton"> < </button>
        <label id = "lblPageNumber" >1</label>
        <button id="nextButton"> > </button>	
        <button id="displayDialogButton"> Update Status </button>	
        <table data-dojo-id="reviewerResultsGrid" data-dojo-type="dojox/grid/DataGrid" autoheight="true" autowidth="true"></table>	 
	<div data-dojo-type="dijit/Dialog" data-dojo-id="statusDialog" title="Update Status" id="statusDialog"  class="reviewerForm" style="width:350px;height: 160px">
            <td><label for="name">Reviewer Technician:</label></td>
            <td><input id="technicianName" placeholder="Technician Name" style="width:170px"/></td>
            <td><label for="address">Lifecycle Status:</label></td>
            <td>  <select id="statusSelect" style="width: 176px" ><option value="">Select Status</option></td>

        <button id="cancelButton" onclick="statusDialog.onCancel();" style="width:80px; float: right;"> Cancel </button>	
        <button id="updateStatusButton" style="width:80px; float: right;"> Update</button>