org.sakaiproject.gradebook.gwt.client.gxt.controller.ServiceController.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.gradebook.gwt.client.gxt.controller.ServiceController.java

Source

/**********************************************************************************
 *
 * $Id:$
 *
 ***********************************************************************************
 *
 * Copyright (c) 2008, 2009 The Regents of the University of California
 *
 * Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 * 
 * http://www.osedu.org/licenses/ECL-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 **********************************************************************************/

package org.sakaiproject.gradebook.gwt.client.gxt.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.sakaiproject.gradebook.gwt.client.AppConstants;
import org.sakaiproject.gradebook.gwt.client.DataTypeConversionUtil;
import org.sakaiproject.gradebook.gwt.client.I18nConstants;
import org.sakaiproject.gradebook.gwt.client.RestBuilder;
import org.sakaiproject.gradebook.gwt.client.RestBuilder.Method;
import org.sakaiproject.gradebook.gwt.client.RestCallback;
import org.sakaiproject.gradebook.gwt.client.action.UserEntityUpdateAction;
import org.sakaiproject.gradebook.gwt.client.gxt.JsonUtil;
import org.sakaiproject.gradebook.gwt.client.gxt.event.GradeMapUpdate;
import org.sakaiproject.gradebook.gwt.client.gxt.event.GradeRecordUpdate;
import org.sakaiproject.gradebook.gwt.client.gxt.event.GradebookEvents;
import org.sakaiproject.gradebook.gwt.client.gxt.event.ItemCreate;
import org.sakaiproject.gradebook.gwt.client.gxt.event.ItemUpdate;
import org.sakaiproject.gradebook.gwt.client.gxt.event.NotificationEvent;
import org.sakaiproject.gradebook.gwt.client.gxt.event.ShowColumnsEvent;
import org.sakaiproject.gradebook.gwt.client.gxt.model.ConfigurationModel;
import org.sakaiproject.gradebook.gwt.client.gxt.model.EntityModel;
import org.sakaiproject.gradebook.gwt.client.gxt.model.EntityOverlay;
import org.sakaiproject.gradebook.gwt.client.gxt.model.ItemModel;
import org.sakaiproject.gradebook.gwt.client.gxt.model.LearnerModel;
import org.sakaiproject.gradebook.gwt.client.gxt.model.LearnerUtil;
import org.sakaiproject.gradebook.gwt.client.model.Configuration;
import org.sakaiproject.gradebook.gwt.client.model.FixedColumn;
import org.sakaiproject.gradebook.gwt.client.model.Gradebook;
import org.sakaiproject.gradebook.gwt.client.model.Item;
import org.sakaiproject.gradebook.gwt.client.model.key.GradeMapKey;
import org.sakaiproject.gradebook.gwt.client.model.key.ItemKey;
import org.sakaiproject.gradebook.gwt.client.model.key.LearnerKey;
import org.sakaiproject.gradebook.gwt.client.model.key.PermissionKey;
import org.sakaiproject.gradebook.gwt.client.model.type.ClassType;
import org.sakaiproject.gradebook.gwt.client.model.type.ItemType;
import org.sakaiproject.gradebook.gwt.client.util.Base64;

import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.Dispatcher;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.TreeStore;
import com.extjs.gxt.ui.client.util.DelayedTask;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;

public class ServiceController extends Controller {

    private DelayedTask showColumnsTask;
    private I18nConstants i18n;

    public ServiceController(I18nConstants i18n) {
        this.i18n = i18n;
        registerEventTypes(GradebookEvents.Configuration.getEventType());
        registerEventTypes(GradebookEvents.CreateItem.getEventType());
        registerEventTypes(GradebookEvents.CreatePermission.getEventType());
        registerEventTypes(GradebookEvents.DeleteGradeMap.getEventType());
        registerEventTypes(GradebookEvents.DeleteItem.getEventType());
        registerEventTypes(GradebookEvents.DeletePermission.getEventType());
        registerEventTypes(GradebookEvents.RevertItem.getEventType());
        registerEventTypes(GradebookEvents.ShowColumns.getEventType());
        registerEventTypes(GradebookEvents.UpdateLearnerGradeRecord.getEventType());
        registerEventTypes(GradebookEvents.UpdateGradeMap.getEventType());
        registerEventTypes(GradebookEvents.UpdateItem.getEventType());
    }

    @Override
    public void handleEvent(AppEvent event) {
        switch (GradebookEvents.getEvent(event.getType()).getEventKey()) {
        case CONFIGURATION:
            onConfigure((ConfigurationModel) event.getData());
            break;
        case CREATE_ITEM:
            onCreateItem((ItemCreate) event.getData());
            break;
        case CREATE_PERMISSION:
            onCreatePermission((ModelData) event.getData());
            break;
        case DELETE_GRADE_MAP:
            onDeleteGradeMap();
            break;
        case DELETE_ITEM:
            onDeleteItem((ItemUpdate) event.getData());
            break;
        case DELETE_PERMISSION:
            onDeletePermission((ModelData) event.getData());
            break;
        case REVERT_ITEM:
            onRevertItem((ItemUpdate) event.getData());
            break;
        case SHOW_COLUMNS:
            onShowColumns((ShowColumnsEvent) event.getData());
            break;
        case UPDATE_LEARNER_GRADE_RECORD:
            onUpdateGradeRecord((GradeRecordUpdate) event.getData());
            break;
        case UPDATE_GRADE_MAP:
            onUpdateGradeMap((GradeMapUpdate) event.getData());
            break;
        case UPDATE_ITEM:
            onUpdateItem((ItemUpdate) event.getData());
            break;
        }
    }

    private void onConfigure(final ConfigurationModel event) {
        doConfigure(event);
    }

    private void doConfigure(final ConfigurationModel model) {

        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);

        Long gradebookId = selectedGradebook.getGradebookId();

        String jsonText = model == null ? null : model.getJSON();

        RestBuilder builder = RestBuilder.getInstance(Method.PUT, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.CONFIG_FRAGMENT, String.valueOf(gradebookId));

        builder.sendRequest(200, 400, jsonText, new RestCallback() {

            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                ModelData m = new ConfigurationModel(overlay);

                Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
                Configuration configModel = selectedGradebook.getConfigurationModel();

                Collection<String> propertyNames = m.getPropertyNames();
                if (propertyNames != null) {
                    List<String> names = new ArrayList<String>(propertyNames);

                    for (int i = 0; i < names.size(); i++) {
                        String name = names.get(i);
                        Object value = m.get(name);
                        configModel.set(name, value);
                    }
                }
            }
        });
    }

    private void onCreateItem(final ItemCreate event) {
        Dispatcher.forwardEvent(GradebookEvents.MaskItemTree.getEventType());

        Gradebook gbModel = Registry.get(AppConstants.CURRENT);

        RestBuilder builder = RestBuilder.getInstance(Method.POST, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.ITEM_FRAGMENT, gbModel.getGradebookUid(),
                String.valueOf(gbModel.getGradebookId()));

        ItemModel model = event.item;
        String jsonText = model == null ? null : model.getJSON();

        builder.sendRequest(200, 400, jsonText, new RestCallback() {

            public void onError(Request request, Throwable exception) {
                super.onError(request, exception);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.HideUserFeedback.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.UnmaskMultiGradeGrid.getEventType());
            }

            public void onFailure(Request request, Throwable exception) {
                Dispatcher.forwardEvent(GradebookEvents.Notification.getEventType(),
                        new NotificationEvent(exception, "Failed to create item: "));
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.HideUserFeedback.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.UnmaskMultiGradeGrid.getEventType());
            }

            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                ItemModel itemModel = new ItemModel(overlay);

                switch (itemModel.getItemType()) {
                case GRADEBOOK:
                    Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
                    selectedGradebook.setGradebookGradeItem(itemModel);
                    Dispatcher.forwardEvent(GradebookEvents.ItemUpdated.getEventType(), itemModel);
                    Dispatcher.forwardEvent(GradebookEvents.RefreshGradebookItems.getEventType(),
                            selectedGradebook);
                    Dispatcher.forwardEvent(GradebookEvents.RefreshCourseGrades.getEventType(), selectedGradebook);
                    break;
                case CATEGORY:
                    if (itemModel.isActive())
                        doCreateItem(event, itemModel);
                    else
                        doUpdateItem(event.store, null, null, itemModel);

                    for (ModelData m : itemModel.getChildren()) {
                        ItemModel item = (ItemModel) m;
                        if (item.isActive())
                            doCreateItem(event, item);
                        else
                            doUpdateItem(event.store, null, null, item);
                    }
                    break;
                case ITEM:
                    if (itemModel.isActive())
                        doCreateItem(event, itemModel);
                    else
                        doUpdateItem(event.store, null, null, itemModel);
                    break;
                }

                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.HideUserFeedback.getEventType());
            }

        });
    }

    private void onCreatePermission(ModelData model) {
        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);

        String gradebookUid = selectedGradebook.getGradebookUid();
        String gradebookId = String.valueOf(selectedGradebook.getGradebookId());

        RestBuilder builder = RestBuilder.getInstance(Method.POST, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.PERMISSION_FRAGMENT, gradebookUid, gradebookId);

        String jsonText = model == null ? null : ((EntityModel) model).getJSON();

        builder.sendRequest(200, 400, jsonText, new RestCallback() {

            @Override
            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                ModelData model = new EntityModel(overlay); //translater.translate(result);
                Dispatcher.forwardEvent(GradebookEvents.PermissionCreated.getEventType(), model);
            }
        });
    }

    private void onDeleteGradeMap() {
        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);

        String gradebookUid = selectedGradebook.getGradebookUid();
        String gradebookId = String.valueOf(selectedGradebook.getGradebookId());

        RestBuilder builder = RestBuilder.getInstance(Method.DELETE, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.GRADE_MAP_FRAGMENT, gradebookUid, gradebookId);

        builder.sendRequest(204, 400, null, new RestCallback() {

            @Override
            public void onSuccess(Request request, Response response) {
                Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
                Dispatcher.forwardEvent(GradebookEvents.RefreshGradeScale.getEventType(), selectedGradebook);
                Dispatcher.forwardEvent(GradebookEvents.RefreshCourseGrades.getEventType(), selectedGradebook);
            }

        });
    }

    private void onDeleteItemSuccess(ItemUpdate event) {
        Dispatcher.forwardEvent(GradebookEvents.ItemDeleted.getEventType(), event.item);
        TreeStore<ItemModel> treeStore = (TreeStore<ItemModel>) event.store;
        ItemModel categoryItemModel = getCategoryItemModel(event.item.getCategoryId());
        treeStore.remove(categoryItemModel, (ItemModel) event.item);
    }

    private void onDeleteItem(final ItemUpdate event) {
        Dispatcher.forwardEvent(GradebookEvents.MaskItemTree.getEventType());

        if (null != event.item) {
            event.item.setRemoved(Boolean.TRUE);
        }

        RestBuilder builder = RestBuilder.getInstance(Method.DELETE, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.ITEM_FRAGMENT);

        ItemModel model = (ItemModel) event.item;
        String jsonText = model == null ? null : model.getJSON();

        builder.sendRequest(200, 400, jsonText, new RestCallback() {

            public void onError(Request request, Throwable exception) {
                super.onError(request, exception);
                onUpdateItemFailure(event, exception);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
            }

            public void onFailure(Request request, Throwable exception) {
                onUpdateItemFailure(event, exception);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
            }

            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                ItemModel itemModel = new ItemModel(overlay);

                Dispatcher.forwardEvent(GradebookEvents.BeginItemUpdates.getEventType());
                onUpdateItemSuccess(event, itemModel);
                onDeleteItemSuccess(event);
                Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
                Dispatcher.forwardEvent(GradebookEvents.EndItemUpdates.getEventType(), selectedGradebook);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
            }
        });
    }

    private void onDeletePermission(ModelData model) {
        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);

        Long gradebookId = selectedGradebook.getGradebookId();

        if (null != model) {
            model.set(PermissionKey.L_GB_ID.name(), gradebookId);
        }

        RestBuilder builder = RestBuilder.getInstance(Method.DELETE, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.PERMISSION_FRAGMENT, selectedGradebook.getGradebookUid());

        String jsonText = model == null ? null : ((EntityModel) model).getJSON();

        builder.sendRequest(200, 400, jsonText, new RestCallback() {

            @Override
            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                ModelData model = new EntityModel(overlay);
                Dispatcher.forwardEvent(GradebookEvents.PermissionDeleted.getEventType(), model);
            }
        });
    }

    private void onUpdateGradeRecordFailure(GradeRecordUpdate event, Throwable caught) {
        Record record = event.record;
        String property = event.property;

        // Save the exception message on the record
        String failedMessage = caught != null && caught.getMessage() != null ? caught.getMessage() : "Failed";
        setFailedFlag(record, property, failedMessage);

        // We have to fool the system into thinking that the value has changed, since
        // we snuck in that "Saving grade..." under the radar.
        if (event.oldValue == null && event.value != null)
            record.set(property, event.value);
        else
            record.set(property, null);

        record.set(property, event.oldValue);

        record.setValid(property, false);

        String message = new StringBuilder().append(i18n.gradeUpdateFailedException()).append(" ")
                .append(record.get(LearnerKey.S_DSPLY_NM.name())).append(". ").toString();

        Dispatcher.forwardEvent(GradebookEvents.Notification.getEventType(),
                new NotificationEvent(caught, message));
        event.onError(event);
    }

    private void setFailedFlag(Record record, String property, String message) {
        String failedProperty = DataTypeConversionUtil.buildFailedKey(property);
        if (record.isModified(failedProperty))
            record.set(failedProperty, null);
        if (message != null)
            record.set(failedProperty, message);
    }

    private void setSuccessFlag(Record record, String property) {
        String successProperty = DataTypeConversionUtil.buildSuccessKey(property);
        if (record.isModified(successProperty))
            record.set(successProperty, null);
        record.set(successProperty, "Success");
    }

    private void onUpdateGradeRecordSuccess(GradeRecordUpdate event, ModelData result) {
        Record record = event.record;
        String property = event.property;

        Collection<String> recordPropertyNames = record.getPropertyNames();
        Collection<String> resultPropertyNames = result.getPropertyNames();

        Set<String> unionPropertyNames = new HashSet<String>();
        if (recordPropertyNames != null)
            unionPropertyNames.addAll(recordPropertyNames);
        if (resultPropertyNames != null)
            unionPropertyNames.addAll(resultPropertyNames);

        if (unionPropertyNames != null) {
            for (String p : unionPropertyNames) {
                // We're only interested in assignment ids here
                if (!LearnerUtil.isFixed(p)) {

                    Object newObj = result.get(p);
                    Object oldObj = record.get(p);

                    if (newObj == null && oldObj != null) {
                        // If the entry is now missing, we want to remove it
                        record.set(p, null);
                    } else {
                        // Otherwise, we simply replace the entry
                        record.set(p, newObj);
                    }
                }
            }
        }

        String courseGrade = result.get(LearnerKey.S_CRS_GRD.name());

        if (courseGrade != null && record.isModified(LearnerKey.S_CRS_GRD.name()))
            record.set(LearnerKey.S_CRS_GRD.name(), null);
        record.set(LearnerKey.S_CRS_GRD.name(), courseGrade);

        String calculatedGrade = result.get(LearnerKey.S_CALC_GRD.name());
        if (calculatedGrade != null && record.isModified(LearnerKey.S_CALC_GRD.name()))
            record.set(LearnerKey.S_CALC_GRD.name(), null);
        record.set(LearnerKey.S_CALC_GRD.name(), calculatedGrade);

        String letterGrade = result.get(LearnerKey.S_LTR_GRD.name());
        if (letterGrade != null && record.isModified(LearnerKey.S_LTR_GRD.name()))
            record.set(LearnerKey.S_LTR_GRD.name(), null);
        record.set(LearnerKey.S_LTR_GRD.name(), letterGrade);

        // Ensure that we clear out any older failure messages
        // Save the exception message on the record
        setFailedFlag(record, property, null);
        setSuccessFlag(record, property);

        record.setValid(property, true);

        // FIXME: Move all this to a log event listener
        StringBuilder buffer = new StringBuilder();
        String displayName = (String) record.get(LearnerKey.S_DSPLY_NM.name());
        if (displayName != null)
            buffer.append(displayName);
        buffer.append(":").append(event.label);

        String message = null;
        if (property.startsWith(AppConstants.COMMENT_TEXT_FLAG)) {
            message = buffer.append("- stored comment as '").append(result.get(property)).append("'").toString();
        } else {
            message = buffer.append("- stored item grade as '").append(result.get(property))
                    .append("' and recalculated course grade to '").append(result.get(LearnerKey.S_CRS_GRD.name()))
                    .append("'").toString();
        }

        Dispatcher.forwardEvent(GradebookEvents.Notification.getEventType(),
                new NotificationEvent("Success", message));
        event.onSuccess(event);
    }

    private void onUpdateGradeMap(final GradeMapUpdate event) {
        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
        final Record record = event.record;

        String gradebookUid = selectedGradebook.getGradebookUid();
        String gradebookId = String.valueOf(selectedGradebook.getGradebookId());
        String letterGrade = (null != record) ? (String) record.get(GradeMapKey.S_LTR_GRD.name()) : null;

        RestBuilder builder = RestBuilder.getInstance(Method.PUT, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.GRADE_MAP_FRAGMENT, gradebookUid, gradebookId,
                letterGrade);

        Double value = record == null ? null : (Double) event.value;
        Double startValue = record == null ? null : (Double) event.startValue;

        JSONObject json = new JSONObject();
        if (value != null)
            json.put(AppConstants.VALUE_CONSTANT, new JSONNumber(value.doubleValue()));
        if (startValue != null)
            json.put(AppConstants.START_VALUE_CONSTANT, new JSONNumber(startValue.doubleValue()));

        builder.sendRequest(204, 400, json.toString(), new RestCallback() {

            @Override
            public void onFailure(Request request, Throwable exception) {
                super.onFailure(request, exception);
                Dispatcher.forwardEvent(GradebookEvents.GradeScaleUpdateError.getEventType());
                record.reject(false);
            }

            @Override
            public void onSuccess(Request request, Response response) {
                Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
                Dispatcher.forwardEvent(GradebookEvents.RefreshGradeScale.getEventType(), selectedGradebook);
                record.commit(false);
            }

        });
    }

    private void onUpdateGradeRecord(final GradeRecordUpdate event) {

        final Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);

        ClassType classType = DataTypeConversionUtil.lookupClassType(event.property,
                selectedGradebook.getGradebookItemModel().getGradeType());

        final Record record = event.record;
        if ((event.oldValue == null || event.oldValue.equals(""))
                && (event.value == null || event.value.equals("")))
            return;

        final UserEntityUpdateAction<ModelData> action = new UserEntityUpdateAction<ModelData>(selectedGradebook,
                record.getModel(), event.property, classType, event.value, event.oldValue);

        String gradebookUid = selectedGradebook.getGradebookUid();
        String entity = null;
        String studentUid = (String) record.getModel().get(LearnerKey.S_UID.name());
        String itemId = (String) event.property;

        JSONObject json = new JSONObject();

        switch (classType) {
        case BOOLEAN:
            if (event.value != null)
                json.put(AppConstants.BOOL_VALUE_CONSTANT,
                        JSONBoolean.getInstance(DataTypeConversionUtil.checkBoolean((Boolean) event.value)));
            if (event.oldValue != null)
                json.put(AppConstants.BOOL_START_VALUE_CONSTANT,
                        JSONBoolean.getInstance(DataTypeConversionUtil.checkBoolean((Boolean) event.oldValue)));
            entity = AppConstants.EXCUSE_FRAGMENT;
            break;
        case STRING:
            if (event.value != null)
                json.put(AppConstants.STR_VALUE_CONSTANT, new JSONString((String) event.value));
            if (event.oldValue != null)
                json.put(AppConstants.STR_START_VALUE_CONSTANT, new JSONString((String) event.oldValue));
            json.put(AppConstants.NUMERIC_FRAGMENT, JSONBoolean.getInstance(false));
            entity = AppConstants.STRING_FRAGMENT;
            break;
        case DOUBLE:
            if (event.value != null)
                json.put(AppConstants.VALUE_CONSTANT, new JSONNumber((Double) event.value));
            if (event.oldValue != null)
                json.put(AppConstants.START_VALUE_CONSTANT, new JSONNumber((Double) event.oldValue));
            json.put(AppConstants.NUMERIC_FRAGMENT, JSONBoolean.getInstance(true));
            entity = AppConstants.NUMERIC_FRAGMENT;
            break;
        }

        if (event.property.startsWith(AppConstants.COMMENT_TEXT_FLAG))
            entity = "comment";

        RestBuilder builder = RestBuilder.getInstance(Method.PUT, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.LEARNER_FRAGMENT, entity, gradebookUid, itemId,
                Base64.encode(studentUid));

        builder.sendRequest(200, 400, json.toString(), new RestCallback() {

            public void onError(Request request, Throwable exception) {
                super.onError(request, exception);
                record.beginEdit();
                onUpdateGradeRecordFailure(event, exception);
                record.endEdit();
            }

            public void onFailure(Request request, Throwable exception) {
                record.beginEdit();
                onUpdateGradeRecordFailure(event, exception);
                record.endEdit();
            }

            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                LearnerModel result = new LearnerModel(overlay);

                record.beginEdit();
                onUpdateGradeRecordSuccess(event, result);
                record.endEdit();

                Dispatcher.forwardEvent(GradebookEvents.LearnerGradeRecordUpdated.getEventType(), action);
            }

        });
    }

    private void onRevertItem(final ItemUpdate event) {
        String property = event.property;
        Record record = event.record;

        record.set(property, null);
        record.set(property, event.oldValue);

        record.setValid(property, false);
    }

    private ShowColumnsEvent lastEvent = null;

    private void onShowColumns(final ShowColumnsEvent event) {
        if (lastEvent != null && event != null && event.equals(lastEvent)) {
            if (showColumnsTask != null)
                showColumnsTask.cancel();
        }

        lastEvent = event;
        showColumnsTask = new DelayedTask(new Listener<BaseEvent>() {

            public void handleEvent(BaseEvent be) {
                doShowColumns(event);
            }
        });

        showColumnsTask.delay(1000);
    }

    private void doShowColumns(ShowColumnsEvent event) {
        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
        ConfigurationModel model = new ConfigurationModel(selectedGradebook.getGradebookId());

        if (event.isSingle) {
            boolean hidden = event.isHidden;

            if (event.isFixed)
                buildColumnConfigModel(model, event.fixedModel, hidden);
            else
                buildColumnConfigModel(model, event.model, hidden);

        } else {

            for (String id : event.fullStaticIdSet) {
                boolean isHidden = !event.visibleStaticIdSet.contains(id);
                buildColumnConfigModel(model, id, isHidden);
            }

        }

        doConfigure(model);
    }

    private void buildColumnConfigModel(Configuration model, String identifier, boolean isHidden) {
        model.setColumnHidden(AppConstants.ITEMTREE, identifier, Boolean.valueOf(isHidden));
    }

    private void buildColumnConfigModel(Configuration model, FixedColumn fixedModel, boolean isHidden) {
        model.setColumnHidden(AppConstants.ITEMTREE, fixedModel.getIdentifier(), Boolean.valueOf(isHidden));
    }

    private void buildColumnConfigModel(Configuration model, ItemModel itemModel, boolean isHidden) {
        switch (itemModel.getItemType()) {
        case GRADEBOOK:
        case CATEGORY:
            for (int i = 0; i < itemModel.getChildCount(); i++) {
                ItemModel child = (ItemModel) itemModel.getChild(i);
                buildColumnConfigModel(model, child, isHidden);
            }
            break;
        case ITEM:
            model.setColumnHidden(AppConstants.ITEMTREE, itemModel.getIdentifier(), Boolean.valueOf(isHidden));
            break;
        }
    }

    private void onUpdateItemFailure(ItemUpdate event, Throwable caught) {

        if (event.record != null) {

            event.record.reject(false);
        }

        Dispatcher.forwardEvent(GradebookEvents.FailedToUpdateItem.getEventType(), event);
        Dispatcher.forwardEvent(GradebookEvents.Notification.getEventType(),
                new NotificationEvent(caught, "Failed to update item: "));
    }

    private void onUpdateItemSuccess(ItemUpdate event, ItemModel result) {

        boolean isCategoryTypeUpdated = false;
        boolean isGradeTypeUpdated = false;
        boolean isGradeScaleUpdated = false;
        boolean isReleaseGradesUpdated = false;
        boolean isReleaseItemsUpdated = false;
        boolean isExtraCreditScaled = false;

        Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);

        if (event.record != null && event.record.isEditing()) {
            Map<String, Object> changes = event.record.getChanges();

            isGradeScaleUpdated = changes != null && changes.get(ItemKey.L_GRD_SCL_ID.name()) != null;
            isGradeTypeUpdated = changes != null && changes.get(ItemKey.G_GRD_TYPE.name()) != null;
            isCategoryTypeUpdated = changes != null && changes.get(ItemKey.C_CTGRY_TYPE.name()) != null;
            isReleaseGradesUpdated = changes != null && changes.get(ItemKey.B_REL_GRDS.name()) != null;
            isReleaseItemsUpdated = changes != null && changes.get(ItemKey.B_REL_ITMS.name()) != null;
            isExtraCreditScaled = changes != null && changes.get(ItemKey.B_SCL_X_CRDT.name()) != null;

            ItemModel item = null;
            ModelData returnedItem = null;
            if (!changes.isEmpty()) {
                item = (ItemModel) event.record.getModel();
                returnedItem = findItemModel(item, result);
            }
            if (null == returnedItem) {
                ///send notification for alert
            } else {
                item.setProperties(returnedItem.getProperties());
            }

            event.record.commit(false);
        } else {
            //// I think this needs a user notification or some other check to make sure that the state is right.
            GWT.log("!!!! swallowed error!!!!\nevent.record=" + event.record + "\nevent.record.isEditing()="
                    + event.record.isEditing() + "\n" + new Date());
        }

        switch (result.getItemType()) {
        case GRADEBOOK:

            Dispatcher.forwardEvent(GradebookEvents.ItemUpdated.getEventType(), result);

            selectedGradebook.setGradebookGradeItem(result);

            if (isCategoryTypeUpdated || isGradeTypeUpdated || isReleaseGradesUpdated || isReleaseItemsUpdated) {
                Dispatcher.forwardEvent(GradebookEvents.RefreshGradebookSetup.getEventType(), selectedGradebook);
            }

            if (isGradeScaleUpdated) {
                Dispatcher.forwardEvent(GradebookEvents.RefreshGradeScale.getEventType(), selectedGradebook);
            }

            if (isGradeTypeUpdated) {
                Dispatcher.forwardEvent(GradebookEvents.GradeTypeUpdated.getEventType(), selectedGradebook);
            }

            if (event.item.getItemType() != ItemType.GRADEBOOK || isGradeTypeUpdated || isCategoryTypeUpdated
                    || isExtraCreditScaled) {
                Dispatcher.forwardEvent(GradebookEvents.RefreshGradebookItems.getEventType(), selectedGradebook);

                Dispatcher.forwardEvent(GradebookEvents.RefreshCourseGrades.getEventType(), selectedGradebook);
            } else if (isGradeScaleUpdated) {
                Dispatcher.forwardEvent(GradebookEvents.RefreshCourseGrades.getEventType(), selectedGradebook);
            } else {
                Dispatcher.forwardEvent(GradebookEvents.RefreshGradebookSetup.getEventType(), selectedGradebook);
            }

            break;
        case CATEGORY:

            doUpdateItem(event, result);

            for (ModelData item : result.getChildren()) {

                doUpdateItem(event, (ItemModel) item);
            }

            if (event.getModifiedItem() != null && event.getModifiedItem().getItemType() != ItemType.CATEGORY)
                return;

            break;
        case ITEM:
            doUpdateItem(event, result);
            break;
        }

    }

    private ModelData findItemModel(ItemModel item, ItemModel result) {

        ModelData rv = null;
        if (result != null) {
            if (result.equals(item)) {
                return result;
            }
            if (!result.getChildren().isEmpty()) {
                List<ModelData> l = result.getChildren();
                int i = l.indexOf(item);
                if (i >= 0) {
                    rv = l.get(i);
                } else {
                    for (ModelData d : l) {
                        rv = findItemModel(item, (ItemModel) d);
                        if (null != rv)
                            break;
                    }
                }
            }
        }
        return rv;

    }

    private void onUpdateItem(final ItemUpdate event) {

        Dispatcher.forwardEvent(GradebookEvents.MaskItemTree.getEventType());

        RestBuilder builder = RestBuilder.getInstance(Method.PUT, GWT.getModuleBaseURL(),
                AppConstants.REST_FRAGMENT, AppConstants.ITEM_FRAGMENT);

        ItemModel model = (ItemModel) event.getModifiedItem();
        String jsonText = model == null ? null : model.getJSON();

        builder.sendRequest(200, 400, jsonText, new RestCallback() {

            public void onError(Request request, Throwable exception) {
                super.onError(request, exception);
                onUpdateItemFailure(event, exception);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.HideUserFeedback.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.UnmaskMultiGradeGrid.getEventType());
            }

            public void onFailure(Request request, Throwable exception) {
                onUpdateItemFailure(event, exception);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.HideUserFeedback.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.UnmaskMultiGradeGrid.getEventType());
            }

            public void onSuccess(Request request, Response response) {

                EntityOverlay overlay = JsonUtil.toOverlay(response.getText());
                ItemModel itemModel = new ItemModel(overlay);

                Dispatcher.forwardEvent(GradebookEvents.BeginItemUpdates.getEventType());
                onUpdateItemSuccess(event, itemModel);
                Gradebook selectedGradebook = Registry.get(AppConstants.CURRENT);
                Dispatcher.forwardEvent(GradebookEvents.EndItemUpdates.getEventType(), selectedGradebook);
                Dispatcher.forwardEvent(GradebookEvents.UnmaskItemTree.getEventType());
                Dispatcher.forwardEvent(GradebookEvents.HideUserFeedback.getEventType());
                if (!event.close) {
                    Dispatcher.forwardEvent(GradebookEvents.StartEditItem.getEventType(), event.item);
                }
            }
        });
    }

    private void doCreateItem(ItemCreate itemCreate, ItemModel createdItem) {
        TreeStore<ItemModel> treeStore = (TreeStore<ItemModel>) itemCreate.store;
        ItemModel categoryItemModel = getCategoryItemModel(createdItem.getCategoryId());
        treeStore.add(categoryItemModel, createdItem, true);
        Dispatcher.forwardEvent(GradebookEvents.ItemCreated.getEventType(), createdItem);
        doUpdatePercentCourseGradeTotal(itemCreate.store, itemCreate.item, createdItem);
    }

    private void doUpdatePercentCourseGradeTotal(Store store, Item oldItem, ItemModel updatedItem) {
        switch (updatedItem.getItemType()) {
        case CATEGORY:
            ItemModel gradebookItemModel = getCategoryItemModel(updatedItem.getCategoryId());
            if (gradebookItemModel != null && gradebookItemModel.getItemType() == ItemType.GRADEBOOK)
                doUpdateItem(store, null, null, gradebookItemModel);
            break;
        }
    }

    private void doUpdateItem(ItemUpdate itemUpdate, ItemModel updatedItem) {
        doUpdatePercentCourseGradeTotal(itemUpdate.store, itemUpdate.item, updatedItem);
        doUpdateItem(itemUpdate.store, itemUpdate.property, itemUpdate.record, updatedItem);
    }

    private void doUpdateItem(Store store, String property, Record record, ItemModel updatedItem) {
        TreeStore<ItemModel> treeStore = (TreeStore<ItemModel>) store;

        if (updatedItem.isActive() && record != null) {
            record.beginEdit();
            for (String p : updatedItem.getPropertyNames()) {
                replaceProperty(p, record, updatedItem);
            }
            record.commit(false);
            Dispatcher.forwardEvent(GradebookEvents.ItemUpdated.getEventType(), updatedItem);
        } else {
            treeStore.update(updatedItem);
        }
    }

    private void replaceProperty(String property, Record record, ItemModel item) {
        Object value = item.get(property);

        record.set(property, null);

        if (value != null)
            record.set(property, value);
    }

    private ItemModel getCategoryItemModel(Long categoryId) {

        Gradebook gradebookModel = Registry.get(AppConstants.CURRENT);
        return (ItemModel) gradebookModel.getCategoryItemModel(categoryId);
    }
}