Please note, this is a STATIC archive of website www.tutorialspoint.com from 11 May 2019, cach3.com does not collect or store any user information, there is no "phishing" involved.
Tutorialspoint

Execute Ruby Online

# Hello World Program in Ruby
puts "Hello World!";

squash

# Hello World Program in Ruby


source_branch = "hotfix/asf"
squash = false

is_release_branch = (/^(release\/).*$/.match(source_branch))
is_hotfix_branch = (/^(hotfix\/).*$/.match(source_branch))
is_master_branch = source_branch == "master"

if is_release_branch
    puts "Is release"
else
    puts "Is not release"
end

if is_hotfix_branch
    puts "Is hotfix"
else
    puts "Is not hotfix"
end

if is_master_branch
    puts "Is master"
else
    puts "Is not master"
end

if !squash and !is_release_branch and !is_hotfix_branch and !is_master_branch 
   puts "Is not avoidable branch" 
end

calculadordeidade

require 'time'

Date_of_birth = '1999-12-15'

def humanize secs
  [ 
    [60, :seconds], 
    [60, :minutes], 
    [24, :hours], 
    [365, :days], 
    [100, :years]
  ].map do |count, name|
    if secs > 0
      secs, n = secs.divmod(count)
      "#{n.to_i} #{name}"
    end
  end.compact.reverse.join(' ')
end

loop do
    distance = Time.new - Time.parse(Date_of_birth)
    print humanize(distance)+"\r"
    sleep 1
end

yehfg

# Hello World Program in Ruby
puts "Build test";
print "123";

asdsad

# Hello World Program in Ruby

#迴圈是從 1到30
for x in 5..10
  if x % 10 == 0
    puts "因為 #{x} 符合了能被10整除 所以就不往下"
    puts "不然 #{x} 也能被5整除 沒餘數的"
    puts "從上往下閱讀 先中的符合 就不在往下執行"
    puts "==========================="
  elsif x % 5 == 0
    puts "該數字為 #{x} 能被5整除 餘數0"
    puts "==========================="
  end
end

Meha

# Hello World Program in Ruby
puts "Hello World!";

puts "1" < "2";
puts "10" < "2";

rubyx

# Hello World Program in Ruby
x = 0;
for i in 1..9
  puts "========第#{i}圈==========="
  puts "現在的 x 為 : #{x}"
  puts "i = #{i} "
  puts "x = #{x} + #{i} "
  x = x+i;
  puts "x == #{x}"
end

puts x

Cracking Coding Interview - Linked List Palindrome

def is_palindrome(root_node)
    current_node = root_node
    prev_node = nil
    while (current_node != nil)
        next_node = current_node.next
        current_node.next = prev_node
        prev_node = current_node
        current_node = next_node
    end
    p prev_node #new root node
    is_palindrome
end

ruby Hausaufgabe

# Hello World Program in Ruby
puts "Hello World!";

class Auto

  #attr_accessor :x, :y, :geschwindigkeit,       #Eigenschaften die unter Klasse Auto definiert werden
      def initialize(x, y)
       @x=x                                     #Position
       @y=y                                     #Position
        @geschwindigkeit=0        #Geschwindigkeiten
      end

     def get_geschwindigkeit()                     #Methode für Klasse definieren
       get_geschwindigkeit=0

    puts get_geschwindigkeit
      end

    def beschleunigen()
     beschleunigen=10

    puts beschleunigen
      end
end


auto_1=Auto.new(20, 40)        #Hauptprogramm
auto_2=Auto.new(20, 80)        #durch .new wird neues Objekt der Klasse erzeugt
auto_3=Auto.new(20, 120)       # in Klammer ruft man Konstruktor oder Initializer auf, Werte werden neuem Objekt mitgegeben

auto_1.beschleunigen
#auto_2.beschleunigen*1
#auto_3.beschleunigen*3

#puts get_geschwindigkeit(auto_1)
#puts get_geschwindigkeit(auto_2)
#puts get_geschwindigkeit(auto_3)

puts auto_1



require 'sourceable' require 'deletable' require 'mass_assignable' require 'by_finders' require 'incident_statistics' require 'cc_extensions' class Incident < Itsm end require 'incident/service_request' require 'incident/incident_events

require 'sourceable'
require 'deletable'
require 'mass_assignable'
require 'by_finders'
require 'incident_statistics'
require 'cc_extensions'
class Incident < Itsm
end

require 'incident/service_request'
require 'incident/incident_events'
class Incident < Itsm
  self.table_name = 'incidents'
  include Sourceable
  include Deletable
  include CustomFields::Model
  include IncidentStatistics
  include CustomDatesParser
  include Trackable
  include LiveStates
  include IncidentTypes
  include CustomUrlHelper
  include CcExtensions
  include ElasticSearch::Scopes::Incident
  include Eventable::Incident
  include IncidentJiraIssue
  include ItsmScopes
  include StripHtml
  include Traversable
  include Incident::Sentiments
  include RealTimeUpdates
  include Automations::Incident
  extend ElasticSearch::IndexFilters::FieldTypes::Incident
  include ElasticSearch::SyncModelFields::Incident
  include EmailHelper

  PREDICT_BY_ATTR_WHITE_LIST = [:name, :created_at, :description, :requester_id, :department_id, :site_id, :account_id, :id, :tag_list, :request_source_id].freeze

  trackable :create, module: "itsm", if: -> incident { incident.number.to_i > 5 }

  serialize :cc, Array

  has_many :holes, -> { where('holes.origin_type="Incident"') }, dependent: :destroy, foreign_key: :affected_id
  has_many_to_many :incidents
  has_one_to_many :problems
  has_many_to_many :itsm_changes, class_name: "Change"
  has_many_to_many :hardwares
  has_many_to_many :purchase_orders
  has_many_to_many :other_assets
  has_many_to_many :mobiles
  has_many_to_many :configuration_items
  has_many :comment_views, through: :comments
  has_many :itsm_life_cycles, as: :itsm

  has_many_to_many \
    :solutions,
    after_add: -> (_incident, solution) { solution.calculate_attached_incidents_count },
    after_remove: -> (_incident, solution) { solution.calculate_attached_incidents_count }

  require 'any_link_smart_extension'

  has_many :events, as: :subject
  has_many :merge_sources, class_name: "Incident", foreign_key: "merge_target_id"
  belongs_to :merge_target, class_name: "Incident", foreign_key: "merge_target_id"

  has_one :ticket_resolution, dependent: :destroy
  has_one :customer_satisfaction_survey, dependent: :destroy
  belongs_to :incident_type, class_name: "IncidentType", foreign_key: :incident_type_id
  belongs_to :incident_sub_type, class_name: "IncidentType", foreign_key: :incident_sub_type_id
  belongs_to :incident_type_level_3, class_name: "IncidentType", foreign_key: :incident_type_level_3_id
  belongs_to :incident_type_level_4, class_name: "IncidentType", foreign_key: :incident_type_level_4_id
  belongs_to :state, class_name: "ItsmState", foreign_key: :state_id
  has_many :customer_satisfaction_surveys
  has_many :new_sla_violations, as: :slable
  has_one :jira_issue, dependent: :destroy
  has_one :normalized_incidents_values
  has_one :incident_resolution
  has_one :prediction_create_incident_type,
          -> { where action: "create", field_name: "incident_type", prediction_algorithm: "elastic_search", predictable_type: "incident" },
          class_name: :Prediction, foreign_key: :predictable_id, dependent: :destroy
  has_one :prediction_update_incident_type,
          -> { where action: "update", field_name: "incident_type", prediction_algorithm: "elastic_search", predictable_type: "incident" },
          class_name: :Prediction, foreign_key: :predictable_id, dependent: :destroy
  has_one :prediction_create_incident_sub_type,
          -> { where action: "create", field_name: "incident_sub_type", prediction_algorithm: "elastic_search", predictable_type: "incident" },
          class_name: :Prediction, foreign_key: :predictable_id, dependent: :destroy
  has_one :prediction_update_incident_sub_type,
          -> { where action: "update", field_name: "incident_sub_type", prediction_algorithm: "elastic_search", predictable_type: "incident" },
          class_name: :Prediction, foreign_key: :predictable_id, dependent: :destroy
  has_one :prediction_create_incident_sub_type_given_type,
          -> { where action: "create", field_name: "incident_sub_type_given_type", prediction_algorithm: "elastic_search", predictable_type: "incident" },
          class_name: :Prediction, foreign_key: :predictable_id, dependent: :destroy
  has_one :prediction_update_incident_sub_type_given_type,
          -> { where action: "update", field_name: "incident_sub_type_given_type", prediction_algorithm: "elastic_search", predictable_type: "incident" },
          class_name: :Prediction, foreign_key: :predictable_id, dependent: :destroy
  accepts_nested_attributes_for :prediction_create_incident_type,
                                :prediction_update_incident_type,
                                :prediction_create_incident_sub_type,
                                :prediction_update_incident_sub_type,
                                :prediction_create_incident_sub_type_given_type,
                                :prediction_update_incident_sub_type_given_type

  TO_HASH_PRELOAD_LIST = [:account, :state, :incident_type, :incident_sub_type, :sla_violations, :assignee, :site, :department, :requester,
                          :custom_fields_values, :comments, :attachments, :shared_attachments, :ticket_resolution, :incidents, :statistics,
                          :itsm_changes, :solutions, :tasks, :time_tracks, :hardwares, :other_assets, :problems, :mobiles, :request_variables, :events
                         ]

  scope :with_custom_past_due_date, -> (datetime) { where("incidents.due_at IS NOT NULL AND due_at < ?", datetime) }
  scope :with_custom_due_in_date, -> (datetime) { where("incidents.due_at IS NOT NULL AND due_at >= ?", datetime) }
  scope :closed_state, -> (state_id) { where("incidents.state_id = ?", state_id) }
  scope :not_closed_state, -> (state_id) { where("incidents.state_id != ?", state_id) }
  scope :unassigned, -> { where "(assignee_id is NULL or assignee_id = -1)" }
  scope :created_after, -> (datetime) { where("incidents.created_at >= ?", datetime) }
  scope :created_before, -> (datetime) { where("incidents.created_at <= ?", datetime) }
  scope :assigned_to, -> (assignee_id) { where("incidents.assignee_id = ?", assignee_id) if assignee_id.to_i > 0 }
  scope :assigned_to_team, -> (assignee_id) {
    joins(:assignee).
      where("groups.superviser_id = ?", assignee_id) if assignee_id.to_i > 0
  }

  scope :sla_violations, -> (violation) {
    where(sanitize_sql_array([
      "EXISTS(SELECT 1 FROM sla_violations WHERE sla_type IN (?) AND slable_id = incidents.id)",
      (violation == ["1"] ? Sla::ALL_DESCRIPTIONS.split(", ") : violation)
    ]))
  }

  scope :sla_violations_resolved, -> (resolved) {
    joins("LEFT OUTER JOIN sla_violations AS sv_resolved ON sv_resolved.slable_id = incidents.id AND sv_resolved.slable_type = 'Incident'").
    group('sv_resolved.slable_id').
    where(["sv_resolved.resolved IN (?)", resolved])
  }

  scope :sla_violations_created_at, -> (datetime) {
    joins("LEFT OUTER JOIN sla_violations AS sv_created ON sv_created.slable_id = incidents.id AND sv_created.slable_type = 'Incident'").
    group('sv_created.slable_id').
    where(['sv_created.created_at >= ?', datetime[0] ? datetime : "1970-01-01"])
  }

  scope :css_grade, -> (grade) {
    joins(:customer_satisfaction_surveys).
    where(customer_satisfaction_surveys: { grade: grade.include?(-777) ? nil : grade.first.split(/\s*,\s*/).map(&:to_bool) })
  }

  # TODO: Not pending approval should return service requests with tasks that aren't pending
  scope :pending_approval, -> (pending_bol) {
    where_phrase = if pending_bol.include?("1") && !pending_bol.include?("0")
      "tasks.id IS NOT NULL"
    elsif pending_bol.include?("0") && !pending_bol.include?("1")
      "tasks.id IS NULL"
    end

    left_joins = "LEFT OUTER JOIN tasks ON
            tasks.taskable_id = incidents.id AND
            tasks.taskable_type = 'Incident' AND
            tasks.completed_at IS NULL AND
            tasks.completed_by = 0 AND
            tasks.confirmation IS true" if where_phrase
    joins(left_joins).where(where_phrase).distinct('incidents.id')
  }

  scope :service_request, -> (cond) { where("incidents.request_source_id IS #{[*cond].first.to_bool ? "NOT NULL" : "NULL"}") }
  scope :with_sla_scope, -> (sla_scopes) { where(sla_scopes.map {|scope| "incidents.#{scope.key} = #{scope.value}"}.join(' AND ')) }
  scope :inactive, -> (datetime) {
    joins("LEFT OUTER JOIN comments cm ON incidents.id = cm.commenter_id AND cm.commenter_type = 'Incident'").
    where('incidents.updated_at < ?', datetime).
    group('incidents.id').
    having('max(cm.updated_at) is NULL OR max(cm.updated_at) < ?', datetime)
  }

  scope :statistics_closed_date, -> (datetimes) {
    no_range_datetimes = datetimes.select { |datetime| not datetime =~ /^[0-9]+$/ }
    range_datetimes = datetimes.select { |date| date =~ /^[0-9]+$/ }
    joins(no_range_datetimes.empty? ? "" : Incident.scope_join(datetimes, "closed")).
    where(no_range_datetimes.empty? ? "" : "statistics_closed_at.value IS NOT NULL").
    where(range_datetimes.empty? ? "" : "statistics.statistics_type_id = 4")
  }

  scope :statistics_resolved_date, -> (datetimes) {
    no_range_datetimes = datetimes.select { |datetime| not datetime =~ /^[0-9]+$/ }
    range_datetimes = datetimes.select { |date| date =~ /^[0-9]+$/ }
    joins(no_range_datetimes.empty? ? "" : Incident.scope_join(datetimes, "resolved")).
    where(no_range_datetimes.empty? ? "" : "statistics_resolved_at.value IS NOT NULL").
    where(range_datetimes.empty? ? "" : "statistics.statistics_type_id = 3")
  }

  scope :statistics_closed_by, -> (id) {
    joins(id ? "LEFT OUTER JOIN statistics AS statistics_closed_by ON statistics_closed_by.statisticable_id = incidents.id" : nil).
      where("statistics_closed_by.statistics_type_id = #{Statistic::STATISTIC_NAMES['to_close']} AND statistics_closed_by.creator_id IN (?)", id)
  }

  scope :statistics_resolved_by, -> (id) {
    joins(id ? "LEFT OUTER JOIN statistics AS statistics_resolved_by ON statistics_resolved_by.statisticable_id = incidents.id" : nil).
      where("statistics_resolved_by.statistics_type_id = #{Statistic::STATISTIC_NAMES['to_resolve']} AND statistics_resolved_by.creator_id IN (?)", id)
  }

  scope :assignee_reports_to, -> (id) {
    where('gr.superviser_id IN (?)', id).
    joins('INNER JOIN groups gr ON gr.id = incidents.assignee_id')
  }

  scope :survey_answered_on_filter, -> (days) {
    joins('LEFT JOIN customer_satisfaction_surveys csso ON csso.incident_id = incidents.id').
    where(due_date_scope(days, "csso.updated_at", "survey_answered_on", true))
  }

  scope :resolution_code_filter, -> (code) {
    joins('LEFT JOIN incident_resolutions ir ON ir.incident_id = incidents.id').
    where('ir.resolution_code IN (?)', code)
  }

  scope :customer_satisfaction_filter, -> (id) { joins(:customer_satisfaction_surveys).where("grade IN (?)", id) }
  scope :due_date_filter, -> (days_from_now) { where(due_date_scope(days_from_now, "incidents.due_at", "due_date")) }
  scope :past_due_date_filter, -> (days_before_now) { where(due_date_scope(days_before_now, "incidents.due_at", "past_due_date", true)) }
  scope :service_catalog, -> (id) { where("request_source_id IS #{id.first} NULL") }
  scope_ransackable :sla_violations_created_at, :sla_violations_resolved, :css_grade, :pending_approval,
                    :checkbox, :statistics_resolved_date, :statistics_closed_date, :due_date_filter, :past_due_date_filter,
                    :assignee_reports_to, :service_catalog, :sla_violations, :service_request, :scheduled,
                    :statistics_closed_by, :statistics_resolved_by, :survey_answered_on_filter, :with_unread_comments, :resolution_code_filter

  scope :itsm_states_count, -> {
    select('COUNT(*) AS count, itsm_states.*, itsm_states.value as name').
    joins(:state).
    where("state_id IS NOT NULL").
    group(:state_id).
    order('itsm_states.order ASC')
  }

  scope :with_open_itsm_states, -> {
    joins(:state).
    where("itsm_states.original_key NOT IN ('#{ItsmState::RESOLVED}', '#{ItsmState::CLOSED}') OR itsm_states.original_key IS NULL")
  }

  scope :in_new_state, -> {
    joins(:state).
    where(itsm_states: { original_key: ItsmState::NEW })
  }

  scope :resolved, -> {
    joins(:state).
    where(itsm_states: { original_key: ItsmState::RESOLVED })
  }

  scope :sla_base_scope, -> (description) {
    case description
    when "not_assigned"
      where(incidents: { assignee_id: [nil, -1] })
    when "not_commented"
      joins("LEFT OUTER JOIN statistics ON statistics.statisticable_id = incidents.id AND
       statistics.statisticable_type = 'Incident' AND
       statistics.statistics_type_id = #{Statistic::STATISTIC_NAMES['to_first_response']}").
      where(statistics: { id: nil })
    when "not_resolved"
      where(nil)
    when "no_actions_taken"
      where("incidents.updated_at = incidents.created_at").includes(:tasks).where(tasks: { id: nil })
    end
  }

  scope :sla_not_breached, -> (sla_id) {
    joins(<<-JOIN
      LEFT OUTER JOIN sla_violations
      ON sla_violations.slable_id = incidents.id
      AND sla_violations.slable_type = 'Incident'
      AND sla_id = #{sla_id}
    JOIN
    ).where(sla_violations: { id: nil })
  }

  scope :with_customer_satisfaction_widget_options, -> (days, site_id, department_id) {
    days_conditions = if days.filter_set?
      days.days.ago
    elsif days == 0
      days.days.ago.midnight
    end
    cond = days_conditions ? ["customer_satisfaction_surveys.updated_at >= ?", days_conditions] : "customer_satisfaction_surveys.updated_at IS NOT NULL"
    site_conditions = { site_id: site_id } if site_id.filter_set?
    department_conditions = { department_id: department_id } if department_id.filter_set?

    eager_load(:customer_satisfaction_surveys).
    where(cond).
    where(site_conditions).
    where(department_conditions)
  }

  scope :hr_incidents_last_30_days, -> (account_id) {
    account_id_cond = ["account_id = ?", account_id]
    incidents_types_ids = IncidentType.all_hr_ids_in_incident_types(account_id)

    incidents_types_cond = ''
    incidents_sub_types_cond = ''
    if incidents_types_ids.present?
      incidents_types_cond = "incident_type_id in ( #{incidents_types_ids} )"
      incidents_sub_types_cond = " or incident_sub_type_id in ( #{incidents_types_ids} )"
    end

    assignee_ids = Group.all_hr_ids_in_groups(account_id)
    assignee_id_cond = ''
    if assignee_ids.present?
      if incidents_types_ids.present?
        assignee_id_cond = ' or '
      end
      assignee_id_cond += "assignee_id in ( #{assignee_ids} )"
    end
    hr_keyword_check = incidents_types_cond + incidents_sub_types_cond + assignee_id_cond
    return 0 if hr_keyword_check.blank?
    where(account_id_cond).
    where(hr_keyword_check).
    where(created_at: (Time.now - 30.days)..Time.now).count
  }

  scope :scheduled, -> (scheduled_bol) { where(scheduled: scheduled_bol.include?("1") ? 1 : nil) }

  scope :order_with_predictions, -> (predictions, order) {
    if predictions.size > 0
      select(sanitize_sql_array(["incidents.*, CASE WHEN id in (#{predictions.join(',')}) THEN id ELSE 0 END AS suggested_order"])).
        order("suggested_order desc, #{order}")
    else
      order(order)
    end
  }

  RESOLUTION = [
    { "Fixed" => 1 }, { "Works_for_me" => 2 }, { "Postponed" => 3 }, { "Duplicate" => 4 }, { "Will_not_fix" => 5 }, { "Invalid" => 6 },
    { "Done" => 7 }, { "Aborted" => 8 }, { "Delivered" => 9 }
  ]

  validates :site, presence: { on: :create, if: :must_specify_site? }
  validates :department, presence: { on: :create, if: :must_specify_department? }
  validates :incident_type, presence: { if: :must_specify_incident_type? }
  validates :resolution_code, presence: { on: :update, if: :must_specify_resolution? }
  validates :incident_sub_type, presence: { if: :must_specify_incident_subtype? }
  validates :priority, exclusion: { in: [nil, -1], on: :create, if: :must_specify_priority?, message: :exclude }
  validate :incident_type_is_not_blank_and_belongs_to_the_account
  validate :incident_sub_type_set_with_incident_type
  validate :incident_sub_type_belongs_to_type
  validate :cc_email_correction
  validate :incident_type_not_deleted, on: :create
  validate :max_cc
  before_create :create_predictions_if_it_comes_from_mail

  after_update :update_assignee_changed
  after_update :modify_count_on_attached_solutions
  after_create :calculate_attached_incidents_count_on_solutions, :increment_catalog_item_request_count
  before_update :set_assigned_state_on_assignee_update, if: :interface_context?

  include Incident::ServiceRequest

  include Events::Publisher

  include Incident::IncidentEvents

  include ElasticSearch::ElasticSearchable

  acts_as_elastic_searchable [
    :name, :description, :incident_type_id, :incident_sub_type_id,
    :requester_id, :assignee_id, :priority, :state_id,
    :site_id, :department_id, :deleted
  ]

  attr_accessor :events_group_uid, :check_mandatory_type, :check_mandatory_sub_type
  attr_accessor :description_has_changed, :previous_state, :editting_context, :prediction_score

  after_initialize :set_events_group_uid

  def redis_key
    "#{Incident.class.name}:{id}"
  end

  def resolution_code
    incident_resolution.try(:resolution_code)
  end

  def resolution
    incident_resolution.try(:resolution)
  end

  def old_resolution
    self[:resolution]
  end

  def update_resolution(resolution_code, resolution)
    resolution_code = resolution_code == -1 ? nil : resolution_code
    updated_incident_resolution = IncidentResolution.find_or_initialize_by(incident_id: id)
    updated_incident_resolution.update_attributes(resolution_code: resolution_code.presence, resolution: resolution.presence)
    self.incident_resolution = updated_incident_resolution
  end

  def check_if_description_changed
    self.description_has_changed = description_changed?
  end

  def create_normalize_data
    if self.description_has_changed
      Resque.enqueue(NormalizedIncidentsValueWorker, id)
    end
  end

  def normalized_incident_description
    clean_html_for_word2vec(description)
  end

  def save_previous_state
    if state_id_changed?
      self.previous_state = changes[:state_id][0]
    end
  end

  def add_itsm_life_record
    if previous_state.present?
      ItsmLifeCycle.create_life_cycle_record(self, previous_state)
    end
  end

  def set_events_group_uid(force_new_id = false)
    self.events_group_uid = nil if force_new_id
    self.events_group_uid ||= Event.generate_events_group_uid
  end

  after_commit :publish_created_event, :create_first_touch_resolution_statistic, on: :create
  after_commit :publish_committed_event, :create_normalize_data, on: [:create, :update]
  after_commit :add_itsm_life_record, on: :update

  def publish_created_event
    initiator = (requester.user || requester) if [ORIGINS[:email], ORIGINS[:email_autogenerated], ORIGINS[:chatter], ORIGINS[:chatter_pm]].include?(origin)
    publish(:incident_created_event, self, initiator || event_user)
  end

  after_update :publish_changed_event
  def publish_changed_event
    changes.each do |key, value|
      # next if resolved? && key == 'state_id' # uncomment when notifications can handle incident resolved event
      event = "incident_#{key}_changed_event".to_sym
      publish(event, self, event_user, serialize_changes_for_events(key))
    end

    serialize_and_publish_resolved_event if just_resolved_or_updated_resolution?
  end

  def publish_committed_event
    publish(:incident_committed_event, self)
  end

  def create_first_touch_resolution_statistic
    statistics.create(value: true, statistics_type_id: Statistic::STATISTIC_NAMES["first_touch_resolution"], account: account) if opened_as_closed?
  end

  def serialize_and_publish_resolved_event
    message_hash = serialize_changes_for_events('state_id').merge(resolution: resolution, resolution_text: resolution_code)
    publish(:incident_resolved_event, self, event_user, message_hash)
  end

  IMPORTABLE = {
    columns: {
      "name" => { name: "Title" },
      "description" => nil,
      "requester" => { klass: 'Requester', foreign_key: "requester_id", attr: :email, scope: :account, allow_nil: true },
      "created_by" => { klass: 'Requester', foreign_key: "created_by_id", attr: :email, readonly: true, scope: :account, allow_nil: true },
      "assignee" => { klass: 'Group', foreign_key: "assignee_id", attr: :email, readonly: true, scope: :account, allow_nil: true },
      "site" => { klass: 'Site', attr: :name, scope: :account, allow_nil: true },
      "department" => { klass: 'Department', attr: :name, scope: :account, allow_nil: true },
      "category" => { klass: 'IncidentType', foreign_key: :incident_type_id, attr: :name, readonly: true, scope: :account, allow_nil: true },
      "subcategory" => {
        klass: 'IncidentType',
        foreign_key: :incident_sub_type_id,
        attr: :name,
        readonly: true,
        scope: :account,
        allow_nil: true,
        context: { parent_id: :incident_type_id }
      },
      "category_level_3" => {
        klass: 'IncidentType',
        foreign_key: :incident_type_level_3_id,
        attr: :name,
        readonly: true,
        scope: :account,
        allow_nil: true,
        context: { parent_id: :incident_sub_type_id }
      },
      "category_level_4" => {
        klass: 'IncidentType',
        foreign_key: :incident_type_level_4_id,
        attr: :name,
        readonly: true,
        scope: :account,
        allow_nil: true,
        context: { parent_id: :incident_type_level_3_id }
      },
      "priority" => nil,
      "state" => { klass: 'ItsmState', foreign_key: :state_id, attr: :value, scope: :account, allow_nil: true, readonly: true },
      "comments" => { has_many: true, klass: 'Comment', parent_entity_attr: :commenter, scope: :incident, allow_nil: true },
      "tag_list" => { name: "Tags", klass: 'Array', attr: :tag_list, separator: ';' }
    },
    origin_title: "Incident",
    origin_path: :incidents_path
  }.freeze

  def opened_as_closed?
    open_as_closed || statistics.for_type(Statistic::STATISTIC_NAMES["first_touch_resolution"]).present?
  end

  def not_closed?
    !(state.resolved? || state.closed?)
  end

  def must_specify_site?
    must_specify_attr?(:site)
  end

  def must_specify_department?
    must_specify_attr?(:department)
  end

  def must_specify_attr?(attribute_name)
    return false unless origin == ORIGINS[:external]
    return false unless account
    account.send("mandatory_incident_#{attribute_name}_for_portal?")
  end

  def must_specify_priority?
    return false unless origin == ORIGINS[:external]
    return false unless account
    account.mandatory_incident_priority_for_portal
  end

  def must_specify_incident_type?
    check_mandatory_type
  end

  def must_specify_incident_subtype?
    check_mandatory_sub_type
  end

  def must_specify_resolution?
    account && state_id == account.itsm_states.resolved.try(:id) && account.mandatory_incident_resolution
  end

  def check_mandatory_type?(user)
    self.check_mandatory_type = if user.helpdesk_user?
      account.mandatory_incident_types_for_portal
    else
      account.mandatory_incident_type
    end
  end

  def check_mandatory_field(user)
    check_mandatory_type?(user)
    check_mandatory_sub_type?(user)
  end

  def check_mandatory_sub_type?(user)
    self.check_mandatory_sub_type = if user.helpdesk_user?
      account.mandatory_incident_subtypes_for_portal
    else
      account.mandatory_incident_subtype
    end && incident_type && incident_type.children.present?
  end

  def check_mandatory_resolution?
    account.mandatory_incident_resolution if account.feature_acquired?(:custom_resolutions)
  end

  def incident_type_not_deleted
    if incident_type && incident_type.deleted?
      error_message = service_request? ? "Please contact your Help Desk Administrator to correct this catalog item's category." : ''
      errors.add(:incident_type, "#{I18n.t('incident_type_not_deleted_error')} #{error_message}")
    end
  end

  def incident_type_is_not_blank_and_belongs_to_the_account
    errors.add(:incident_type, I18n.t("incident_type_is_not_blank_and_belongs_to_the_account_error")) if incident_type && incident_type.account_id != account_id
  end

  def incident_sub_type_set_with_incident_type
    if incident_sub_type && incident_type.blank?
      errors.add(:incident_sub_type, I18n.t("incident_sub_type_set_without_incident_type_error"))
    end
  end

  def incident_sub_type_belongs_to_type
    if incident_sub_type_id.to_i > 0 && incident_sub_type && incident_type && incident_sub_type.parent.id != incident_type.id
      errors.add(:incident_sub_type, I18n.t("incident_sub_type_belongs_to_type_error"))
    end
  end

  def active_task_status
    return unless service_request?
    pending_activity_ids = workflow && workflow.root.running_activities.select { |activity| activity.try(:type) == "Approval" }.map(&:id)
    if pending_activity_ids.present?
      pending_workflow_approvers = account.workflow_approvers.where(approval_id: pending_activity_ids).where("vote IS NULL")
      pending_assignee_ids = pending_workflow_approvers.map(&:assignee_id).compact.uniq
      account.groups.where(id: pending_assignee_ids).map(&:name).to_sentence
    end
  end

  def self.scope_join(datetimes, type)
    scope_join = "LEFT OUTER JOIN statistics AS statistics_#{type}_at ON statistics_#{type}_at.statisticable_id = incidents.id \
    AND statistics_#{type}_at.statistics_type_id = #{type == 'resolved' ? '3' : '4'} "

    dates = scope_datetimes(datetimes, type)
    scope_join += "AND (" + dates + ")" if dates.present?
  end

  def self.scope_datetimes(datetimes, type)
    scope_datetimes = ""
    datetimes = datetimes.select { |datetime| not datetime =~ /^[0-9]+$/ }
    datetimes.each_with_index do |datetime, index|
      scope_datetimes << "statistics_#{type}_at.value > '#{datetime}' "
      scope_datetimes << "OR " if datetimes.size > 1 && index < datetimes.size - 1
    end
    scope_datetimes
  end

  ORIGINS = { web: 1, external: 2, system: 3, email: 4, api: 5, mobile: 6, chatter: 7, chatter_pm: 8, scheduled: 9, email_autogenerated: 10 }
  ORIGINS_VALUES = ORIGINS.inject({}) { |h, k| h[k.second] = k.first; h }

  include MassAssignable
  include IndexCellData::IncidentCellData

  STATE_OPTIONS = [
    { name: ItsmState::NEW, id: ItsmState::NEW.downcase, live_state: true },
    { name: ItsmState::ASSIGNED, id: ItsmState::ASSIGNED.downcase, live_state: true },
    { name: ItsmState::AWAITING_INPUT, id: ItsmState::AWAITING_INPUT.downcase, live_state: false },
    { name: ItsmState::ON_HOLD, id: ItsmState::ON_HOLD.downcase, live_state: false },
    { name: ItsmState::RESOLVED, id: ItsmState::RESOLVED.downcase, live_state: false },
    { name: ItsmState::CLOSED, id: ItsmState::CLOSED.downcase, live_state: false }
  ]
  STATES = STATE_OPTIONS.inject({}) { |h, k| h[k[:id]] = k[:name]; h }
  LIVE_STATES = STATE_OPTIONS.map { |state| state[:id] if state[:live_state] }.compact

  CSS_OPTIONS = [
    {name: "Satisfied", id: "true"},
    {name: "Not_Satisfied", id: "false"},
    {name: "Answered", id: "true, false"},
  ]

  OPTIONS_CSS = CSS_OPTIONS.inject({}){|h, k| h[k[:id]] = k[:name]; h}
  OPTIONS_CSS.default = ""

  MAX_EMAIL_NUMBER = 50

  EMAIL_DELAY_TIME = 2.minutes

  validate :state_in_account, if: -> (incident) { incident.state_id_changed? }

  acts_as_mass_assignable [
    { attr: :state_id,
      type: :select,
      source: lambda { |account| StatesCollection.new(account).options_for_mass_assign },
      translate: false,
      title: "State",
      url: 'states_collection_index_path' },
    { attr: :incident_type_id, type: :select2, url: :types_list_incident_types_path, title: "Category" },
    { attr: :incident_sub_type_id, type: :select2, url: :types_list_incident_types_path, title: "Sub_Category" },
    { attr: :incident_type_level_3_id, type: :select2, url: :types_list_incident_types_path, title: "Category_Level_3" },
    { attr: :incident_type_level_4_id, type: :select2, url: :types_list_incident_types_path, title: "Category_Level_4" },
    { attr: :assignee_id, type: :select2, url: :group_list_groups_path, title: "Assigned_To", staff: true, is_user: true, payload: { staff: true } },
    { attr: :resolution_code,
      type: :select,
      translate: true,
      title: 'Resolution_code',
      options: lambda { |account| account.resolution_codes.sort_by(&:order).map { |p| [ResolutionCode.display_value(p), p.resolution_code] } } },
    { attr: :priority,
      type: :select,
      source: lambda { |account| PRIORITY.map { |id, name| { name: name, id: id } } },
      translate: true,
      title: "Priority",
      options: PRIORITY.map { |id, name| { id: id, name: I18n.t(name.underscorize) } } },
    { attr: :due_at, type: :date, title: "Due_Date" },
    { attr: :site_id, type: :select2, url: :sites_path, title: I18n.t("Site") },
    { attr: :department_id, type: :select2, url: :departments_path, title: I18n.t("Department") },
    { attr: :add_to_tag_list, type: :text, title: "Add_Tags" },
    { attr: :remove_from_tag_list, type: :text, title: "Remove_Tags" }
  ]

  # Used for automation conditions and actions
  acts_as_traversable [
    { attr: :site_id, url: '/sites.json', condition: Automations::Conditions::MultiIds, action: Automations::Actions::ChangeSite },
    { attr: :department_id, url: '/departments.json', condition: Automations::Conditions::MultiIds, action: Automations::Actions::ChangeDepartment },
    { attr: :incident_type_id, title: 'Category', url: '/incident_types/types_list.json', condition: Automations::Conditions::MultiIds, action: Automations::Actions::ChangeCategory },
    { attr: :incident_sub_type_id, title: 'Subcategory', url: '/incident_types/sub_types_list.json', condition: Automations::Conditions::Subcategory, action: Automations::Actions::ChangeSubCategory },
    { attr: :priority,
      sort_by_value: true,
      options: PRIORITY.to_a.reverse.to_h,
      condition: Automations::Conditions::MultiOptions,
      action: Automations::Actions::ChangePriority
    },
    { attr: :requester, menu: true, condition: Automations::Conditions::Menu },
    { attr: :keyword, condition: Automations::Conditions::Keyword },
    { attr: :assignee_id, url: '/users.json', action: Automations::Actions::ChangeAssignee },
    { attr: :state_id, condition: Automations::Conditions::ItsmState, action: Automations::Actions::ItsmState },
    { attr: :due_at, action: Automations::Actions::ChangeDueDate },
    { attr: :origin,
      options: ORIGINS_VALUES.reject { |k, _| [7, 8, 10].include?(k) }.transform_values { |v| "incident_origins.#{v}" },
      condition: Automations::Conditions::MultiOptions
    },
    {
      attr: :request_source_id,
      title: 'ServiceRequest',
      options: ['Service_Request_No', 'Service_Request_Yes'].map { |v| [v, v] }.to_h,
      condition: Automations::Conditions::Type
    }
  ]

  acts_as_csv :id, :number,
    { name: :state_string, title: "state_string", custom_view_column: :state },
    { name: :name, title: "title", custom_view_column: :title },
    { name: :priority_string, custom_view_column: :priority },
    { name: :origin_string, custom_view_column: :origin, title: "incident_origin" },
    { name: :incident_type, custom_view_column: :type, title: "category" },
    { name: :incident_sub_type, custom_view_column: :sub_type, title: "subcategory" },
    { name: :description, format: :strip_html, truncate: true },
    { name: :assignee, format: :embedded, custom_view_column: :assigned_to },
    :requester,
    :created_by,
    { name: :due_at, custom_view_column: :due_date },
    :created_at,
    { name: :created_at, custom_view_column: :created_at_with_time, title: "created_at_with_time" },
    :updated_at,
    { name: :updated_at, custom_view_column: :updated_at_with_time, title: "updated_at_with_time" },
    { name: :tag_list, concat: true },
    { name: :problem_id, custom_view_column: :problem },
    { name: :itsm_change_ids, title: "Changes", concat: true, custom_view_column: :changes },
    :site, :department, :custom_fields, :statistics, :request_variables,
    { name: :hardware_ids, concat: true, format: :embedded },
    { name: :other_asset_ids, concat: true, format: :embedded },
    { name: :solution_ids, concat: true, format: :embedded },
    { name: :incident_ids, concat: true, format: :embedded },
    { name: :comments, concat: true, format: :embedded, truncate: true },
    { name: :associated_sla_names, concat: true, format: :embedded },
    { name: :customer_satisfaction_response, title: "customer_satisfaction_feedback", custom_view_column: :customer_satisfaction_feedback },
    { name: :customer_satisfied_yes_no, custom_view_column: :customer_satisfied? },
    :total_time_spent,
    { name: :resolution_code, custom_view_column: :resolution_type, title: 'resolution_text' },
    :resolution, :scheduled, :resolved_at, :closed_at,
    { name: :resolved_by, format: :embedded },
    { name: :sla_violations, title: "slm_breaches", concat: true, format: :embedded },
    { name: :attachments_links, title: "attachments_links", custom_view_column: :attachments_links },
    :price,
    { name: :active_task_status, title: "pending_approval", concat: true, format: :embedded, custom_view_column: :pending_approval },
    { name: :asset_string, title: "asset", custom_view_column: :asset },
    { name: :cc, concat: true }

  attr_accessor :create_solution

  after_save :create_new_solution, :check_if_description_changed, :save_previous_state
  before_save :assign_default_tags_from_incident_type, :state_statistics
  # Using prepend in order to make this run before the acts_as_eventable's before_save, to include these changes in audit
  before_save :set_default_values, prepend: true
  before_validation :update_sub_type, on: :update
  before_update :resolve_no_actions_taken_sla
  before_create :assign_default_tags_from_site_and_department
  after_create :create_state_changed_statistic
  before_create :set_origin, :set_message_id_if_needed
  before_validation :validate_and_set_state, on: :create

  include ByFinders
  by_finder :incident_type, true, 'state_id, name'
  by_finder :incident_sub_type, true, 'state_id, name, incident_type_id'
  by_finder :priority, false, "incident_type_id"
  by_finder :assignee, "assignee", "incident_type_id"
  by_finder :requester, "requester", "incident_type_id"
  by_finder :site
  by_finder :department

  def update_sub_type
    self.incident_sub_type_id = -1 if changed.include?("incident_type_id") && !changed.include?("incident_sub_type_id")
  end

  def associated_sla_names
    self.sla_violations.map(&:sla_type)
  end

  def breached_sla_rules_ids
    sla_violations.map(&:sla_id)
  end

  def created_via_text
    case origin
    when 9
      " according to scheduled recurring service request"
    when 7
      " via Chatter"
    when 6
      " via Mobile"
    when 5
      " via API"
    when 4
      " via Email"
    when 3
      " by System"
    when 2
      " via Portal"
    when 1
      " via Web"
    else
      nil
    end
  end

  def create_new_solution
    return unless create_solution == "1"
    return unless state.resolved? || opened_as_closed?
    return if current_controller.try(:current_ability) && !current_controller.current_ability.can?(:create, Solution)

    solution = self.solutions.build(name: name)
    solution.incidents << self
    solution.account_id = account_id
    solution.creator_id = current_controller.send(:current_user).try(:id)
    solution.description = <<-RUBY
Resolution:
#{resolution}

Description:
#{description}

Comments:
#{comments.map { |c| "By #{c.username} on #{c.created_at.strftime('%m/%d/%y %H:%I')}\n#{c.body}"}.join("\n\n")}
    RUBY

    solution.save
  end

  def after_comment_created(comment, keep_incident_closed = false)
    publish(:incident_commented_event, self, comment.user || comment.requester, { comment: comment.body, is_private: comment.is_private?, comment_id: comment.id })
    return if Account.testing_account?((comment.user || comment.requester).try(:account)) || comment.from_autogenerated_email?
    if comment.is_private? && assignee.present?
      DisableCallbacksOption.enqueue(NotifyOnChangeWorker, { action: :comment, incident_id: id, events_group_uid: events_group_uid, comment_id: comment.id, private: true })
    elsif !comment.is_private?
      DisableCallbacksOption.enqueue(NotifyOnChangeWorker, { action: :comment, incident_id: id, events_group_uid: events_group_uid, comment_id: comment.id })
      reopen_incident unless keep_incident_closed
    end
    send_mention_email_if_needed(comment)
  end

  def send_mention_email_if_needed(comment)
    body = comment[:body]
    return unless body
    mentions = get_mentions_from_comment(body)
    publish_mentions_events(mentions, body, comment.id, comment.is_private?)
  end

  def publish_mentions_events(mentions, body, comment_id, is_private)
    mentions.uniq.each do |mention|
      group = account.groups.find_by(id: mention)
      next if !group
      initiator_user = account.users.find_by(id: current_user_id)
      set_events_group_uid(true)
      publish(:incident_mentioned_event, self, initiator_user, { comment: body, is_private: is_private, comment_id: comment_id, group_id: group.id })
      group.memberships.each do |member|
        DisableCallbacksOption.enqueue(NotifyOnChangeWorker, { action: :mention, incident_id: id, events_group_uid: events_group_uid,
           comment_id: comment_id, mention_recipient_id: member.user_id })
      end
    end
  end

  def publish_css_event(token, css_id)
    publish(:customer_satisfaction_survey_event, self, system_initiator, { token: token, css_id: css_id })
  end

  def after_task_event(task, created: true, reassigned_from: nil)
    details = {
      task: task.name,
      old_assignee: reassigned_from,
      task_assignee: task.assignee_id,
      task_due_at: task.due_at.try(:strftime, "%m/%d/%Y"),
      task_id: task.id,
      is_process: service_request?,
      confirm_token: task.confirm_token
    }
    kind, initiator = if created
             [(task.confirmation? ? :approval_task_created_event : :task_created_event), (task.requester unless service_request?)]
           else
             [:task_reassigned_event, event_user]
           end
    publish(kind, self, initiator || system_initiator, details)
  end

  def set_message_id_if_needed
    self.original_message_id ||= EmailID.generate_message_id
  end

  def reopen_incident
    if !state.active? && account.reopen_commented_incidents
      set_events_group_uid(true)
      self.state_id = account.itsm_states.send(assignee.present? ? :get_assigned : :get_new).id
      save!
    end
  end

  def merge_into(target, actor, source_msg = nil, target_msg = nil)
    target.copy_ccs_from!(self)
    incidents << target if incidents.where(id: target.id).blank?
    post_merge_comment(actor, source_msg) if source_msg
    target.post_merge_comment(actor, target_msg, target: true) if target_msg
    update_tag_list(true, [I18n.t("merge_incidents.Merged", locale: account.language)])
    self.state_id = account.itsm_states.get_closed.id
    self.merge_target_id = target.id
    save!
  rescue => e
    err_msg = "Could not merge incidents: source: #{id}, target: #{target.id}. (#{e})"
    Log.write!(err_msg, action: 'Merge Incidents', level: :error, source: :incidents, account_id: account_id)
    false
  end

  def post_merge_comment(actor, message, target: false)
    comment_attrs = {
      body: message,
      user: actor,
      is_private: target,
      keep_incident_closed: true,
      origin: Comment::ORIGINS[:system]
    }
    comments.create!(comment_attrs)
  end

  def guess_user_email
    assignee ? assignee.email : nil
  end

  def self.state_string(state_id)
    ItsmState.find_by(id: state_id).try(:display_value)
  end

  def just_resolved_or_updated_resolution?(changes_hash = changes)
    changes_hash.symbolize_keys.slice(:state_id, :resolution, :resolution_code).present? && state.resolved?
  end

  def state_string
    state.display_value
  end

  def resolution_code=(resolution_code)
    self.update_resolution(resolution_code, nil)
  end

  def description
    description_new
  end

  def description=(desc)
    self.description_new = desc
  end

  def set_default_values
    set_default_incident_types
    set_default_assignee
  end

  def set_default_incident_types
    if incident_type_id.to_i < 1
      self.incident_type_id = account.default_incident_type_enabled && account.defaut_incident_type && account.default_incident_type_id || -1
    end
    if incident_sub_type_id.to_i < 1
      self.incident_sub_type_id = -1
    end
  end

  def set_default_assignee
    self.assignee_id = current_controller.try(:current_user).try(:group).try(:id) if assignee_id.to_i < 1 && opened_as_closed?
    self.assignee_id = incident_sub_type.try(:default_assignee_id) if assignee_id.to_i < 1
    self.assignee_id = incident_type.try(:default_assignee_id) if assignee_id.to_i < 1
    self.assignee_id = site.try(:default_assignee_id) if assignee_id.to_i < 1
    self.assignee_id = department.try(:default_assignee_id) if assignee_id.to_i < 1
    self.assignee_id = account.default_incident_assignee_id if assignee_id.to_i < 1 && account.default_incident_assignee_enabled && account.default_incident_assignee
    self.assignee_id = -1 unless assignee_id
  end

  def private_comment_recipients(comment)
    return [] unless assignee.present? && account.assignee_commented
    assignees = assignee.users.enabled
    assignees -= [comment.user] unless account.assignee_own
    assignees
  end

  def should_send_survey?
    return false unless account.customer_surveys_for_incident_type(incident_type_id)
    return false if customer_satisfaction_survey
    return false if CustomerSatisfactionSurvey.where({ incident_id: id }).present?
    return false if account.id == SystemWideConstants::Account::AUTOTESTS_ACCOUNT_ID && !name.include?('satisfaction survey test')
    return false unless frequency_limit_expired?
    true
  end

  def frequency_limit_expired?
    customer_surveys_delay = account.customer_surveys_delay.to_i
    customer_surveys_frequency = account.customer_surveys_frequency.to_i
    scheduled_time = customer_surveys_delay.hours.from_now
    customer_surveys_frequency == 0 || (requester.customer_satisfaction_survey_time || Time.new(1970, 1, 1)) + customer_surveys_frequency.hours < scheduled_time
  end

  attr_accessor :skip_email, :sla_action, :skip_statistics, :open_as_closed

  def from_autogenerated_email?
    origin == ORIGINS[:email_autogenerated]
  end

  def self.notify_on_change(opts, index = 1)
    incident = find_with_deleted(opts[:incident_id], includes: :account)
    account = incident.account
    audit = if opts[:change]
      change = YAML.load(opts[:change].to_s)
      Audit.new(
        created_at: change[:time],
        action: change[:action],
        audit_changes: change[:changes],
        auditable_type: "Incident",
        auditable_id: opts[:incident_id]
      )
    end
    incident.with_time_zone do
      actor = if opts[:action] == :comment
                comment = Comment.where(id: opts[:comment_id]).first
                return unless comment
                comment.user || comment.requester
              elsif opts[:user_id]
                User.where(id: opts[:user_id]).first
              elsif opts[:requester_id]
                Requester.where(id: opts[:requester_id]).first
              elsif opts[:action] == :mention
                User.find_by(id: opts[:mention_recipient_id])
              else
                nil
              end
      if opts[:private]
        incident.private_comment_recipients(comment).each do |recipient|
          Notifier.incident_email(incident, recipient, opts[:events_group_uid], { recipient_type: :assignee }).deliver
        end
      else
        if opts[:action] == :resolution && incident.should_send_survey?
          incident.create_customer_satisfaction_survey(token: Samanage.generate_token).send_survey(incident.requester)
        end

        action = case opts[:action]
                 when :other
                   return unless incident.should_deliver_incident_change?(audit)

                   if audit.action == 1
                     if incident.assignee_id.to_i < 1 && account.default_incident_notification_enabled && account.notify_unassigned_incidents_user_id.to_i > 0
                       group = Group.find_by_id(account.notify_unassigned_incidents_user_id)
                       if group.present?
                         group.users.enabled.each do |user|
                           Notifier.incident_email(incident, user, opts[:events_group_uid], { recipient_type: :assignee }).deliver
                         end
                       end
                     end
                     :received
                   else
                     :changed
                   end
                 when :resolution
                   :resolved
                 when :comment
                   :commented
                 when :mention
                   :mentioned
                 end

        return if action == :received && incident.number.to_i <= 5

        email_requester, assignees, cc_names, mention = account.notification_recipients(incident, action, actor, audit)

        relevent_assignees = assignees[((index - 1) * MAX_EMAIL_NUMBER)..(index * MAX_EMAIL_NUMBER - 1)] || []

        relevent_cc_names = cc_names[((index - 1) * MAX_EMAIL_NUMBER)..(index * MAX_EMAIL_NUMBER - 1)] || []

        unless opts[:sla_violation_event]
          if relevent_assignees.length > 0
            relevent_assignees.each do |recipient|
              Notifier.incident_email(incident, recipient, opts[:events_group_uid], { recipient_type: :assignee }).deliver
            end
          end

          if (mention)
            Notifier.incident_email(incident, mention, opts[:events_group_uid], { mention: true }).deliver
          end
        end

        if email_requester && index == 1
          Notifier.incident_email(incident, email_requester, opts[:events_group_uid], { cc_names: relevent_cc_names, recipient_type: :requester }).deliver
        elsif relevent_cc_names.any?
          email_opts = { cc_names: relevent_cc_names, ccs_only: true, recipient_type: :requester }
          Notifier.incident_email(incident, incident.requester.user || incident.requester, opts[:events_group_uid], email_opts).deliver
        end

        if [assignees.length, cc_names.length].max > (index * MAX_EMAIL_NUMBER)
          Resque.enqueue_at(Time.now + EMAIL_DELAY_TIME, NotifyOnChangeWorker, opts, index + 1)
        end
      end

      if incident.origin == ORIGINS[:chatter]
        if action == :resolved
          account.chatter.try(:post, incident)
        elsif action == :commented
          account.chatter.try(:post, incident, comment)
        end
      end
    end
  rescue ActiveRecord::RecordNotFound => e
    raise e if account && (account.id != SystemWideConstants::Account::AUTOTESTS_ACCOUNT_ID)
  end

  def past_due_date?
    due_at && due_at < Time.now.utc
  end

  def self.origin_string(origin)
    origin ? ORIGINS_VALUES[origin] : nil
  end

  def origin_string
    self.class.origin_string(origin)
  end

  def self.origin_to_display(origin)
    str = ORIGINS_VALUES[origin.to_i]
    str ? I18n.t("incident_origins.#{str}") : ''
  end

  def customer_satisfied?
    if customer_satisfaction_survey.try(:grade)
      "Customer is satisfied"
    elsif self.customer_satisfaction_survey.try(:grade) == false
      "Customer is not satisfied"
    end
  end

  def attachments_links
    attachments_list = ""
    attachments.each do |attachment|
      unless attachments_list.empty?
        attachments_list << "\n"
      end
      attachments_list << attachment.get_link(force_absolute: true)
    end

    attachments_list
  end

  def customer_satisfied_yes_no
    if customer_satisfaction_survey.try(:grade)
      "Yes"
    elsif self.customer_satisfaction_survey.try(:grade) == false
      "No"
    end
  end

  def should_deliver_incident_change?(audit)
    return false unless audit.audit_changes && audit.audit_changes.is_a?(Hash)

    # we handle this situation with Notifier.deliver_incident_resolved
    return false if audit.audit_changes[:status] && audit.audit_changes[:status].last == "closed"

    # we don't care if other fields are changed
    return false if (Account::INCIDENT_NOTIIFER_CHANGABLE_ATTRIBUTES & audit.audit_changes.keys).blank?

    return false if state.closed? && audit.action == 3 # dont send email on audit incident close - we have separate handler for this
    true
  end

  def to_search_results
    {
      "Number" => number,
      "State" => state_string,
      "Priority" => priority_string,
      "Category" => incident_type.try(:name),
      "Subcategory" => incident_sub_type.try(:name),
      "Assigned_to" => assignee.try(:name),
      "Created" => created_at.strftime("%x"),
      "Due_date" => due_at.try(:strftime, "%x"),
      "Comments" => comments.count,
      "Solution" => resolution,
      "Site" => site.try(:name),
      "Department" => department.try(:name),
      "Requester" => requester.try(:name),
      "Updated" => updated_at.try(:strftime, "%x")
    }
  end

  include Apiable
  api_writer :site, :department, :incident_type, :incident_sub_type, category: [:readonly, :incident_type], subcategory: [:readonly, :incident_sub_type],
    category_level_3: [:readonly, :incident_type_level_3], category_level_4: [:readonly, :incident_type_level_4],
    assets: [:readonly, :hardwares], other_assets: [:readonly, :other_assets], tasks: :writeonly, time_tracks: :writeonly, incidents: :readonly,
    solutions: :readonly, assignee: :readonly, problem: [:readonly, :problems], changes: [:readonly, :itsm_changes], requester: :readonly, comments: :readonly,
    configuration_items: [:readonly, :configuration_items]
  api_writer_values_locator state: { key: :state_id, finder: -> value, account { account.itsm_states.by_type("Incident").by_value(value).try(:first).try(:id) } }

  def to_hash(options = {})
    res = add_base_hash_values
    add_extra_hash_values(res, options) if options[:layout] != :mobile_app
    res
  end

  def resolution?
    @has_resolution ||= (resolution_code.present? || resolution.present?)
  end

  def fields_for_email
    fields = []
    catalog_item = CatalogItem.unscoped.find(request_source_id)
    request_variables.each do |variable|
      if variable.kind == CatalogItemVariable::ATTACHMENT
        fields << { name: variable.name,
                    value: { name: variable.attachment.attachment.original_filename[0..29],
                             url: variable.attachment.attachment.url
                           }
                  } if variable.attachment.try(:attachment)
      else
        fields << { name: variable.name, value: h(variable.humanized_value).gsub(/\r?\n/, '<br>').html_safe }
      end
    end
    fields << { name: "Price", value: catalog_item.price_string } if catalog_item.show_price?
    fields << { name: "Delivery Time", value: catalog_item.due_days }
    fields << { name: "Assigned To", value: assignee.try(:name) }
    fields << { name: "Category", value: incident_type.try(:name) }
    fields << { name: "Subcategory", value: incident_sub_type.try(:name) }
    fields << { name: "Priority", value: priority_string }
    fields << { name: "Due", value: due_at.try(:to_s, :human) }
    fields << { name: "Site", value: site.try(:name) }
    fields << { name: "Department", value: department.try(:name) }
    fields.delete_if { |x| x[:value].nil? }
  end

  def assign_default_tags_from_site_and_department
    return unless account.allow_autotagging?
    sd_tags = [site.try(:name), department.try(:name)].compact
    update_tag_list(true, sd_tags) unless sd_tags.blank?
  end

  def assign_default_tags_from_incident_type
    add_tags = []
    if incident_type_id_changed? && incident_type
      add_tags.concat(incident_type.default_tags.gsub(", ", ",").split(/,/))
    end
    if incident_sub_type_id_changed? && incident_sub_type
      add_tags.concat(incident_sub_type.default_tags.gsub(", ", ",").split(/,/))
    end
    return if add_tags.blank?
    self.update_tag_list(true, add_tags)
  end

  def resolve_no_actions_taken_sla
    self.sla_violations.for_type("no_actions_taken").first.try(:resolve)
  end

  def self.breach_date_filter(dates)
    select('sv.created_at').each do |sla_violation|
      dates.map {|date| date[:count] += 1 if (sla_violation.present? && sla_violation.created_at >= date["id"].to_i.days.ago) || date["id"] = '-1'}
    end
    dates
  end

  def statistics_by_type(stat, stat_type)
    stat.statistics_type_id == Statistic.statistic_names[stat_type].to_i
  end

  def resolved_at
    time_to_present("to_resolve")
  end

  def resolved_by
    statistic = statistics.detect { |stat| stat.statistics_type_id == 3 && stat.creator_id.present? }
    return nil unless statistic
    User.find_by(id: statistic.creator_id)
  end

  def first_touch_resolution_at
    statistic = statistics.detect { |stat| stat.statistics_type_id == 7 }
    return '' unless statistic

    statistic.created_at
  end

  def closed_at
    closed_at = time_to_present("to_close").presence
    closed_at ||= first_touch_resolution_at
    closed_at
  end

  def time_to_present(stat_type)
    stats = statistics.detect { |stat| statistics_by_type(stat, stat_type) }

    return '' unless stats
    Time.zone.parse(stats.value)
  end

  def self.accessible_by(ability, action = :read)
    conditions = Ability.adjust_conditions(ability.model_adapter(Incident, action).conditions, "incidents", "incidents")
    cc_sql_condition = {}
    if conditions.is_a?(Hash) && conditions.key?("incidents.cc")
      cc_sql_condition = conditions.delete("incidents.cc")
    end
    where(conditions).where(cc_sql_condition)
  end

  def clone_service_request cloned_incident
    return unless cloned_incident.service_request?
    self.currency = cloned_incident.currency
    self.price = cloned_incident.price
    self.request_source_id = cloned_incident.request_source_id
    self.request_status = "in progress"
    self.workflow = Workflows::Workflow.new(root: request_source.workflow_template.root)
    self.request_variables = cloned_incident.request_variables.map(&:dup)
  end

  def save_with_retry
    tries ||= 3
    save!
  rescue ActiveRecord::StatementInvalid => e
    reset_changed_attrs_counter
    sleep Random.rand(5.0)
    (e.message.include?("Duplicate entry") && (tries -= 1).nonzero?) ? retry : (raise e)
  end

  def price_string
    "#{sprintf('%.2f', price)} #{currency}" if price
  end

  def asset_string
    (hardwares.map { |hw| h(hw.name) } + other_assets.map { |oa| h(oa.name) }).compact.join(', ')
  end

  def calculate_incident_type_prediction
    calculate_incident_type_prediction_from_type
  end

  def calculate_incident_sub_type_given_prediction
    return empty_prediction_details if incident_type_id.to_i <= 0
    prediction = prediction_update_incident_sub_type_given_type || prediction_create_incident_sub_type_given_type
    return empty_prediction_details unless prediction
    incident_type = IncidentType.find_by(id: prediction.predicted_value.to_i)
    return empty_prediction_details if !incident_type || incident_type.parent_id != incident_type_id
    create_prediction_details(prediction)
  end

  def show_predictions?(current_account)
    return false if current_account.blank?
    current_account.show_predictions?
  end

  def updated_sub_type_top_predictions
    prediction_update_incident_sub_type_given_type.try(:top_three_predictions)
  end

  def predict_field(options)
    field = options[:field]
    client = options[:client]
    params = (options[:additional_query] || {}).merge!({ object: self })
    ability = options[:ability]

    res = []
    return res if insufficient_data_for_prediction?
    if SystemSetting::ElasticSearch.predict_enabled?
      elasticsearch_results = ElasticSearch::Tasks::IncidentAttrPredictor.new(
        client,
        account,
        field_to_predict: field
      ).predict(params)
      results_handler = ElasticSearch::ResultHandlers::IncidentsResultHandler.new(account, field_to_predict: field)
      formatted_results = results_handler.format_results(elasticsearch_results, threshold: 10, calculate_confidence: true)
      formatted_results[:scores].each do |prediction|
        incident_type_id = prediction.first
        score = prediction.second[:score]
        confidence = (prediction.second[:confidence] || 0).round(2)
        next unless incident_type_permitted?(ability, incident_type_id)
        break if res.size >= Prediction::MAX_PREDICTION_SIZE || score <= 0
        incident_types = account.incident_types.active
        incident_type = incident_types.find_by(id: incident_type_id)
        next unless incident_type
        break if list_size_requires_break(res, incident_types, incident_type)
        res << { incident_type: incident_type, confidence: confidence}
      end
    end
    res
  end

  def incident_type_permitted?(ability, incident_type_id)
    return true unless ability
    conditions = ability.model_adapter(Incident, id ? :edit : :create).conditions
    return true unless (conditions.is_a?(String) && conditions.match(/^not \((.+)\)$/))
    conditions_arr = Regexp.last_match(1).split(' AND ')
    !match_condition(conditions_arr, incident_type_id)
  end

  def match_condition(conditions_arr, incident_type_id)
    conditions_arr.any? do |cond|
      cond.match(/^`incidents`\.`incident_(sub_)?type_id` (=|IN) [(]*([^()]+)[)]*$/)
      ids_arr = Regexp.last_match(3).to_s.split(", ")
      ids_arr.include?(incident_type_id.to_s)
    end
  end

  def insufficient_data_for_prediction?
    return name.blank? && description.blank?
  end

  def list_size_requires_break(res, incident_types, incident_type)
    list_size = incident_types.size
    parent_incident_type = incident_type.parent
    if parent_incident_type
      list_size = parent_incident_type.children.size
    end
    res.size >= (list_size / 2).floor
  end

  def states_for_model
    account.itsm_states.map do |state|
      {
        id: state.id,
        key: state.original_key,
        title: state.display_value,
        color: state.color,
        selected: state_id == state.id,
        archived: state.archived
      }
    end.flatten
  end

  def assign_editting_context(context)
    self.editting_context = context
  end

  protected
  def set_origin
    self.origin ||= 1 # Web
  end

  def validate_and_set_state
    if state.nil?
      self.state_id = account.itsm_states.get_new.id
    elsif !state.new? && !state.closed?
      errors.add(:state, I18n.t("validate_and_set_state_error"))
    elsif state.closed?
      self.open_as_closed = true
    end
  end

  def external_request?
    return false unless current_controller
    current_controller.send(:portal?)
  end

  def event_user
    return current_controller.send(:current_user) if current_controller && current_controller.send(:current_user) # can't use try since current_user is private
    system_initiator
  end

  def system_initiator
    { name: "System", account_id: requester.account_id }
  end

  def state_in_account
    errors.add(:state, I18n.t("state_in_account_error")) unless valid_state?
  end

  def valid_state?
    return false if !account
    state = account.itsm_states.where(id: state_id).first
    state && !state.archived
  end

  def customer_satisfaction_response
    customer_satisfaction_survey.try(:response).try(:body)
  end

  private

  def create_prediction_details(prediction)
    top_predictions = prediction.top_three_predictions || "[]"
    generate_prediction_details(top_predictions)
  end

  def empty_prediction_details
    generate_prediction_details("[]")
  end

  def was_prediction_ever_updated?
    prediction_update_incident_type != nil
  end

  def calculate_incident_type_prediction_from_type
    prediction = prediction_update_incident_type || prediction_create_incident_type
    return empty_prediction_details unless prediction
    generate_prediction_details(prediction.top_three_predictions)
  end

  def generate_prediction_details(top_predictions)
    { top_predictions: top_predictions || "[]" }
  end

  def add_base_hash_values
    result = _base_hash_values
    use_incident_type_as_category(result) if current_controller.try(:request).try(:api_version) < 1.2
    result
  end

  def _base_hash_values
    {
      id: id,
      number: number,
      name: name,
      description: description,
      description_no_html: html_to_text(description),
      state: state_string,
      priority: priority_string,
      category: incident_type,
      subcategory: incident_sub_type,
      assignee: assignee,
      requester: requester,
      created_at: created_at,
      updated_at: updated_at,
      due_at: due_at,
      sla_violations: sla_violations,
      number_of_comments: comments.count,
      user_saw_all_comments: Comment.user_saw_all_comments(self),
      is_service_request: service_request?,
      created_by: created_by
    }.merge(account.feature_enabled?(FourLevelCategory) ? { category_level_3: incident_type_level_3, category_level_4: incident_type_level_4} : {})
  end

  def use_incident_type_as_category(result)
    result[:incident_type] = result.delete(:category)
    result[:incident_sub_type]  = result.delete(:subcategory)
  end

  def add_extra_hash_values(result, options)
    result.merge!(
      custom: custom,
      href: current_controller.send(:incident_url, self, format: current_controller.params[:format]),
      site: site,
      department: department,
      cc: cc,
      custom_fields_values: custom_fields_values_with_api_versioning
    )

    result[:price] = price_string if service_request?

    options[:layout] == :long ? add_long_layout_values(result, options) : add_short_layout_values(result)
  end

  def add_long_layout_values(result, options)
    result.merge!(
      comments: comments,
      attachments: attachments,
      statistics: statistics_api_information(statistics),
      tags: tags,
      incidents: collection_api_urls(:incidents),
      changes: collection_api_urls(:itsm_changes, :change),
      solutions: collection_api_urls(:solutions),
      associated_sla_names: associated_sla_names,
      is_customer_satisfied: customer_satisfied_yes_no,
      customer_satisfaction_response: customer_satisfaction_response,
      total_time_spent: total_time_spent,
      resolution: resolution,
      resolution_type: resolution_code,
      tasks: get_tasks(options),
      time_tracks: collection_api_urls(:time_tracks),
      assets: collection_api_urls(:hardwares),
      mobiles: collection_api_urls(:mobiles),
      other_assets: collection_api_urls(:other_assets),
      configuration_items: collection_api_urls(:configuration_items),
      purchase_orders: collection_api_urls(:purchase_orders)
    )

    result[:audits] = audits if !options[:mobile_app] && current_controller.action_name != 'index'

    if options[:audit_archive] == 'true' && current_controller.action_name != 'index'
      result['audit_archives'] = Audit.unscoped.archive.where(['auditable_id = ? and auditable_type = ?', id, 'Incident']).to_a
    end

    result[:problem] = get_problem if problem

    if current_controller.try(:request).try(:api_version) > 1
      result[:request_variables] = request_variables
    else
      result[:request_variables] = Hash[request_variables.map { |v| [v.name, v.value]}] if service_request?
    end

    result[:cc_groups] = cc_groups if cc.present?
    result[:completed] = !workflow.active? if service_request?
    result[:no_process] = true if service_request? && !workflow.activities_exists?
  end

  def get_tasks(options)
    options[:mobile_app] && service_request? ? mobile_workflow_tasks : collection_api_urls(:tasks)
  end

  def mobile_workflow_tasks
    workflow.root.running_activities.map { |activity| mobile_new_workflow_task(activity) }
  end

  def increment_catalog_item_request_count
    request_source.update_column :requested_count, request_source.requested_count + 1 if request_source
  end

  def mobile_new_workflow_task(activity)
    data = activity.data
    if activity.type == 'Action'
      {
        id: activity.id,
        name: data[:name],
        decription: data[:description],
        assignee_id: data[:assignee_id],
        assignee: assignee
      }
    elsif activity.type == 'Approval'
      {
        name: data[:name],
        decription: data[:description],
        due_days: data[:due_in],
        approval_condition: data[:approval_condition],
        tasks: new_workflow_approval_tasks(data[:workflow_approver_ids])
      }
    end
  end

  def new_workflow_approval_tasks(approver_ids)
    Workflows::Activities::WorkflowApprover.find(approver_ids).map { |approver| new_workflow_approver_hash(approver) }
  end

  def new_workflow_approver_hash(approver)
    {
      id: approver.id,
      assignee_id: approver.assignee_id,
      assignee: approver.assignee,
      completed_at: approver.completed_at,
      completed_by: approver.completed_by,
      confirmed_at: approver.vote == Workflows::ACCEPT ? Workflows::ACCEPT : "",
      rejected_at: approver.vote == Workflows::DECLINE ? Workflows::DECLINE : "",
      response: approver.response.try(:body)
    }
  end

  def get_problem
    {
      id: problem.id,
      href: current_controller.send(:problem_url, problem.id, format: current_controller.params[:format]),
      name: problem.name,
      number: problem.number
    }
  end

  def add_short_layout_values(result)
    result.merge!(
      incidents: collection_api_urls(:incidents),
      changes: collection_api_urls(:itsm_changes, :change),
      tasks: collection_api_urls(:tasks),
      time_tracks: collection_api_urls(:time_tracks),
      solutions: collection_api_urls(:solutions),
      assets: collection_api_urls(:hardwares),
      mobiles: collection_api_urls(:mobiles),
      other_assets: collection_api_urls(:other_assets),
      configuration_items: collection_api_urls(:configuration_items),
      purchase_orders: collection_api_urls(:purchase_orders)
    )
  end

  def create_predictions_if_it_comes_from_mail
    return unless origin == 4

    client = ElasticSearch::Client.new
    predict_field_params = { field: "incident_type_id", client: client }
    top_predictions = []
    predict_field(predict_field_params).each do |prediction|
      top_predictions << { id: prediction[:incident_type].id, confidence: prediction[:confidence] }
    end

    if top_predictions.empty?
      predicted_value = "-1"
      prediction_confidence = "0.0"
    else
      predicted_value = top_predictions[0][:id]
      prediction_confidence = top_predictions[0][:confidence]
    end

    self.prediction_create_incident_type = Prediction.new(
      action: 'create',
      predictable_type: 'Incident',
      field_name: 'incident_type',
      field_value: "-1",
      prediction_algorithm: 'elastic_search',
      predicted_value: predicted_value,
      prediction_confidence: prediction_confidence,
      top_three_predictions: top_predictions.to_json
    )
  end

  def cc_groups
    emails = cc.map { |email| email[/<(.*)>/, 1] }.compact
    [].tap do |groups|
      account.groups.where("groups.email IN (?)", emails).each do |group|
        groups << group.to_hash(groups_data: true, with_email: true)
      end
    end
  end

  def calculate_attached_incidents_count_on_solutions
    solutions.each(&:calculate_attached_incidents_count)
  end

  def modify_count_on_attached_solutions
    return unless deleted_changed? && deleted
    calculate_attached_incidents_count_on_solutions
  end

  def interface_context?
    editting_context == :interface
  end

  def set_assigned_state_on_assignee_update
    if assignee_id_changed? && state_id == account.itsm_states.send(:get_new).id && !state_id_changed?
      self.state_id = account.itsm_states.send(:get_assigned).id
    end
  end
end

Advertisements
Loading...

We use cookies to provide and improve our services. By using our site, you consent to our Cookies Policy.