# 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
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
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
# 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' 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
We use cookies to provide and improve our services. By using our site, you consent to our Cookies Policy. Accept Learn more