<commit_before>define([
  "Backbone",
  "Underscore",
  "models/Task",
  "models/TaskCollection"
], function(Backbone, _, Task, TaskCollection) {
  function ValidationError(attribute, message) {
    this.attribute = attribute;
    this.message = message;
  }

  var DEFAULT_HEALTH_MSG = "Unknown";
  var EDITABLE_ATTRIBUTES = ["cmd", "constraints", "container", "cpus", "env",
    "executor", "id", "instances", "mem", "disk", "ports", "uris"];
  var VALID_ID_PATTERN = "^(([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])\\.)*([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])$";
  var VALID_ID_REGEX = new RegExp(VALID_ID_PATTERN);

  function findHealthCheckMsg(healthCheckResults, context) {
    return healthCheckResults.map(function (hc, index) {
      if (hc && !hc.alive) {
        var failedCheck = this.get("healthChecks")[index];
        return "Warning: Health check '" +
          (failedCheck.protocol ? failedCheck.protocol + " " : "") +
          (this.get("host") ? this.get("host") : "") +
          (failedCheck.path ? failedCheck.path : "") + "'" +
          (hc.lastFailureCause ?
            " returned with status: '" + hc.lastFailureCause + "'" :
            " failed") +
          ".";
      }
    }, context);
  }

  return Backbone.Model.extend({
    defaults: function() {
      return {
        cmd: null,
        constraints: [],
        container: null,
        cpus: 0.1,
        env: {},
        executor: "",
        healthChecks: [],
        id: null,
        instances: 1,
        mem: 16.0,
        disk: 0.0,
        ports: [0],
        uris: []
      };
    },
    initialize: function(options) {
      _.bindAll(this, 'formatTaskHealthMessage');
      // If this model belongs to a collection when it is instantiated, it has
      // already been persisted to the server.
      this.persisted = (this.collection != null);

      this.tasks = new TaskCollection(null, {appId: this.id});
      this.on({
        "change:id": function(model, value, options) {
          // Inform TaskCollection of new ID so it can send requests to the new
          // endpoint.
          this.tasks.options.appId = value;
        },
        "sync": function(model, response, options) {
          this.persisted = true;
        }
      });
    },
    isNew: function() {
      return !this.persisted;
    },
    allInstancesBooted: function() {
      return this.get("tasksRunning") === this.get("instances");
    },
    formatTasksRunning: function() {
      var tasksRunning = this.get("tasksRunning");
      return tasksRunning == null ? "-" : tasksRunning;
    },
    formatTaskHealthMessage: function(task) {
      if (task) {
        var msg;

        switch(task.getHealth()) {
          case Task.HEALTH.HEALTHY:
            msg = "Healthy";
            break;
          case Task.HEALTH.UNHEALTHY:
            var healthCheckResults = task.get("healthCheckResults");
            if (healthCheckResults != null) {
              msg = findHealthCheckMsg(healthCheckResults, this);
            }
            break;
          default:
            msg = DEFAULT_HEALTH_MSG;
            break;
        }

        return msg;
      }
      return null;
    },
    /* Sends only those attributes listed in `EDITABLE_ATTRIBUTES` to prevent
     * sending immutable values like "tasksRunning" and "tasksStaged" and the
     * "version" value, which when sent prevents any other attributes from being
     * changed.
     */
    save: function(attrs, options) {
      options || (options = {});

      var allAttrs;
      if (options.patch === true) {
        allAttrs = _.extend({}, this.changedAttributes, attrs);
      } else {
        allAttrs = _.extend({}, this.attributes, attrs);
      }

      // Filter out null and undefined values
      var filteredAttributes = _.filter(EDITABLE_ATTRIBUTES, function(attr) {
        return allAttrs[attr] != null;
      });

      var allowedAttrs = _.pick(allAttrs, filteredAttributes);

      /* When `options.data` is supplied, Backbone does not attempt to infer its
       * content type. It must be explicitly set for the content to be
       * interpreted as JSON.<commit_msg>Adding hasSaveError function in model<commit_after>define([
  "Backbone",
  "Underscore",
  "models/Task",
  "models/TaskCollection"
], function(Backbone, _, Task, TaskCollection) {
  function ValidationError(attribute, message) {
    this.attribute = attribute;
    this.message = message;
  }

  var DEFAULT_HEALTH_MSG = "Unknown";
  var EDITABLE_ATTRIBUTES = ["cmd", "constraints", "container", "cpus", "env",
    "executor", "id", "instances", "mem", "disk", "ports", "uris"];
  var VALID_ID_PATTERN = "^(([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])\\.)*([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])$";
  var VALID_ID_REGEX = new RegExp(VALID_ID_PATTERN);

  function findHealthCheckMsg(healthCheckResults, context) {
    return healthCheckResults.map(function (hc, index) {
      if (hc && !hc.alive) {
        var failedCheck = this.get("healthChecks")[index];
        return "Warning: Health check '" +
          (failedCheck.protocol ? failedCheck.protocol + " " : "") +
          (this.get("host") ? this.get("host") : "") +
          (failedCheck.path ? failedCheck.path : "") + "'" +
          (hc.lastFailureCause ?
            " returned with status: '" + hc.lastFailureCause + "'" :
            " failed") +
          ".";
      }
    }, context);
  }

  return Backbone.Model.extend({
    defaults: function() {
      return {
        cmd: null,
        constraints: [],
        container: null,
        cpus: 0.1,
        env: {},
        executor: "",
        healthChecks: [],
        id: null,
        instances: 1,
        mem: 16.0,
        disk: 0.0,
        ports: [0],
        uris: []
      };
    },
    initialize: function(options) {
      _.bindAll(this, 'formatTaskHealthMessage');
      // If this model belongs to a collection when it is instantiated, it has
      // already been persisted to the server.
      this.persisted = (this.collection != null);

      this.tasks = new TaskCollection(null, {appId: this.id});
      this.on({
        "change:id": function(model, value, options) {
          // Inform TaskCollection of new ID so it can send requests to the new
          // endpoint.
          this.tasks.options.appId = value;
        },
        "sync": function(model, response, options) {
          this.persisted = true;
        }
      });
    },
    isNew: function() {
      return !this.persisted;
    },
    allInstancesBooted: function() {
      return this.get("tasksRunning") === this.get("instances");
    },
    formatTasksRunning: function() {
      var tasksRunning = this.get("tasksRunning");
      return tasksRunning == null ? "-" : tasksRunning;
    },
    formatTaskHealthMessage: function(task) {
      if (task) {
        var msg;

        switch(task.getHealth()) {
          case Task.HEALTH.HEALTHY:
            msg = "Healthy";
            break;
          case Task.HEALTH.UNHEALTHY:
            var healthCheckResults = task.get("healthCheckResults");
            if (healthCheckResults != null) {
              msg = findHealthCheckMsg(healthCheckResults, this);
            }
            break;
          default:
            msg = DEFAULT_HEALTH_MSG;
            break;
        }

        return msg;
      }
      return null;
    },
    hasSaveError: function(obj, response) {
      if(this.validationError == null) {
        this.validationError = [];
      }
      // handling success as well
      if (response.status < 300) {
        return false;
      } else if (response.status === 422) {
        this.validationError.push(
          new ValidationError("id", "An app with this ID already exists")
        );
        return true;
      } else if (response.status >= 500) {
        this.validationError.push(
          new ValidationError("general", "Server error, could not create")
        );
        return true;
      } else {
        this.validationError.push(
          new ValidationError("general", "Creation unsuccessful")
        );
        return true;
      }
    },
    /* Sends only those attributes listed in `EDITABLE_ATTRIBUTES` to prevent
     * sending immutable values like "tasksRunning" and "tasksStaged" and the
     * "version" value, which when sent prevents any other attributes from being
     * changed.
     */
    save: function(attrs, options) {
      options || (options = {});

      var allAttrs;
      if (options.patch === true) {
        allAttrs = _.extend({}, this.changedAttributes, attrs);
      } else {
        allAttrs = _.extend({}, this.attributes, attrs);
      }

      // Filter out null and undefined values
      var filteredAttributes = _.filter(EDITABLE_ATTRIBUTES, function(attr) {
        return allAttrs[attr] != null;
      });

      var allowedAttrs = _.pick(allAttrs, filteredAttributes);

      /* When `options.data` is supplied, Backbone does not attempt to infer its
       * content type. It must be explicitly set for the content to be
       * interpreted as JSON.
{'fraction_non_alphanumeric': 0.13568896765618077, 'fraction_numerical': 0.006535223748338502, 'mean_word_length': 2.1803451919690033, 'pattern_counts': {'":': 4, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1950653', 'n_tokens_mistral': 2706, 'n_tokens_neox': 2641, 'n_words': 646}