Moved signature detection to transaction module.

This commit is contained in:
Martin Edenhofer 2016-04-22 08:55:10 +02:00
parent 2d93b435ba
commit e56ed8eeca
12 changed files with 192 additions and 142 deletions

View file

@ -1,33 +0,0 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
require 'signature_detection'
class Observer::Ticket::Article::SignatureDetection < ActiveRecord::Observer
observe 'ticket::_article'
def before_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# if sender is not customer, do not change anything
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
return if !sender
return if sender['name'] != 'Customer'
# set email attributes
type = Ticket::Article::Type.lookup(id: record.type_id)
return if type['name'] != 'email'
# add queue job to update current signature of user id
Delayed::Job.enqueue(Observer::Ticket::Article::SignatureDetection::BackgroundJob.new(record.created_by_id))
# user
user = User.lookup(id: record.created_by_id)
return if !user
return if !user.preferences
return if !user.preferences[:signature_detection]
record.preferences[:signature_detection] = SignatureDetection.find_signature_line(user.preferences[:signature_detection], record.body)
end
end

View file

@ -1,9 +0,0 @@
class Observer::Ticket::Article::SignatureDetection::BackgroundJob
def initialize(id)
@user_id = id
end
def perform
SignatureDetection.rebuild_user(@user_id)
end
end

View file

@ -1,7 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Observer::Transaction < ActiveRecord::Observer class Observer::Transaction < ActiveRecord::Observer
observe :ticket, 'ticket::_article' observe :ticket, 'ticket::_article', :user, :organization
def self.commit(params = {}) def self.commit(params = {})
@ -28,10 +28,10 @@ class Observer::Transaction < ActiveRecord::Observer
# get uniq objects # get uniq objects
list_objects = get_uniq_changes(list) list_objects = get_uniq_changes(list)
list_objects.each {|_id, item| list_objects.each {|_object, objects|
objects.each {|_id, item|
# send background job Delayed::Job.enqueue(Transaction::BackgroundJob.new(item, params))
Delayed::Job.enqueue(Transaction::BackgroundJob.new(item, params)) }
} }
end end
@ -40,33 +40,37 @@ class Observer::Transaction < ActiveRecord::Observer
result = get_uniq_changes(events) result = get_uniq_changes(events)
result = { result = {
1 => { 'Ticket' =>
object: 'Ticket', 1 => {
type: 'create', object: 'Ticket',
ticket_id: 123, type: 'create',
article_id: 123, object_id: 123,
}, article_id: 123,
9 => { },
object: 'Ticket', 9 => {
type: 'update', object: 'Ticket',
ticket_id: 123, type: 'update',
changes: { object_id: 123,
attribute1: [before, now], changes: {
attribute2: [before, now], attribute1: [before, now],
} attribute2: [before, now],
},
},
}, },
} }
result = { result = {
9 => { 'Ticket' =>
object: 'Ticket', 9 => {
type: 'update', object: 'Ticket',
ticket_id: 123, type: 'update',
article_id: 123, object_id: 123,
changes: { article_id: 123,
attribute1: [before, now], changes: {
attribute2: [before, now], attribute1: [before, now],
} attribute2: [before, now],
},
},
}, },
} }
@ -76,57 +80,59 @@ class Observer::Transaction < ActiveRecord::Observer
list_objects = {} list_objects = {}
events.each { |event| events.each { |event|
# get current state of objects # simulate article create as ticket update
if event[:name] == 'Ticket::Article' article = nil
if event[:object] == 'Ticket::Article'
article = Ticket::Article.lookup(id: event[:id]) article = Ticket::Article.lookup(id: event[:id])
# next if article is already deleted
next if !article next if !article
next if event[:type] == 'update'
ticket = article.ticket # set new event infos
if !list_objects[ticket.id] ticket = Ticket.lookup(id: article.ticket_id)
list_objects[ticket.id] = {} event[:object] = 'Ticket'
event[:id] = ticket.id
event[:type] = 'update'
event[:changes] = nil
end
# get current state of objects
object = Kernel.const_get(event[:object]).lookup(id: event[:id])
# next if object is already deleted
next if !object
if !list_objects[event[:object]]
list_objects[event[:object]] = {}
end
if !list_objects[event[:object]][object.id]
list_objects[event[:object]][object.id] = {}
end
store = list_objects[event[:object]][object.id]
store[:object] = event[:object]
store[:object_id] = object.id
if !store[:type] || store[:type] == 'update'
store[:type] = event[:type]
end
# merge changes
if event[:changes]
if !store[:changes]
store[:changes] = event[:changes]
else
event[:changes].each {|key, value|
if !store[:changes][key]
store[:changes][key] = value
else
store[:changes][key][1] = value[1]
end
}
end end
list_objects[ticket.id][:object] = 'Ticket' end
list_objects[ticket.id][:article_id] = article.id
list_objects[ticket.id][:ticket_id] = ticket.id
if !list_objects[ticket.id][:type] # remember article id if exists
list_objects[ticket.id][:type] = 'update' if article
end store[:article_id] = article.id
elsif event[:name] == 'Ticket'
ticket = Ticket.lookup(id: event[:id])
# next if ticket is already deleted
next if !ticket
if !list_objects[ticket.id]
list_objects[ticket.id] = {}
end
list_objects[ticket.id][:object] = 'Ticket'
list_objects[ticket.id][:ticket_id] = ticket.id
if !list_objects[ticket.id][:type] || list_objects[ticket.id][:type] == 'update'
list_objects[ticket.id][:type] = event[:type]
end
# merge changes
if event[:changes]
if !list_objects[ticket.id][:changes]
list_objects[ticket.id][:changes] = event[:changes]
else
event[:changes].each {|key, value|
if !list_objects[ticket.id][:changes][key]
list_objects[ticket.id][:changes][key] = value
else
list_objects[ticket.id][:changes][key][1] = value[1]
end
}
end
end
else
raise "unknown object for integration #{event[:name]}"
end end
} }
list_objects list_objects
@ -138,7 +144,7 @@ class Observer::Transaction < ActiveRecord::Observer
return if Setting.get('import_mode') return if Setting.get('import_mode')
e = { e = {
name: record.class.name, object: record.class.name,
type: 'create', type: 'create',
data: record, data: record,
id: record.id, id: record.id,
@ -151,9 +157,6 @@ class Observer::Transaction < ActiveRecord::Observer
# return if we run import mode # return if we run import mode
return if Setting.get('import_mode') return if Setting.get('import_mode')
# ignore updates on articles / we just want send integrations on ticket updates
return if record.class.name == 'Ticket::Article'
# ignore certain attributes # ignore certain attributes
real_changes = {} real_changes = {}
record.changes.each {|key, value| record.changes.each {|key, value|
@ -173,7 +176,7 @@ class Observer::Transaction < ActiveRecord::Observer
return if real_changes.empty? return if real_changes.empty?
e = { e = {
name: record.class.name, object: record.class.name,
type: 'update', type: 'update',
data: record, data: record,
changes: real_changes, changes: real_changes,

View file

@ -6,7 +6,7 @@ class Transaction::Notification
{ {
object: 'Ticket', object: 'Ticket',
type: 'update', type: 'update',
ticket_id: 123, object_id: 123,
via_web: true, via_web: true,
changes: { changes: {
'attribute1' => [before, now], 'attribute1' => [before, now],
@ -21,9 +21,15 @@ class Transaction::Notification
end end
def perform def perform
# return if we run import mode
return if Setting.get('import_mode')
return if @item[:object] != 'Ticket'
return if @params[:disable_notification] return if @params[:disable_notification]
ticket = Ticket.find(@item[:ticket_id]) ticket = Ticket.find(@item[:object_id])
if @item[:article_id] if @item[:article_id]
article = Ticket::Article.find(@item[:article_id]) article = Ticket::Article.find(@item[:article_id])
end end

View file

@ -0,0 +1,58 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
require 'signature_detection'
class Transaction::SignatureDetection
=begin
{
object: 'Ticket',
type: 'update',
object_id: 123,
via_web: true,
changes: {
'attribute1' => [before, now],
'attribute2' => [before, now],
}
},
=end
def initialize(item, params = {})
@item = item
@params = params
end
def perform
# return if we run import mode
return if Setting.get('import_mode')
return if @item[:type] != 'create'
return if @item[:object] != 'Ticket'
ticket = Ticket.lookup(id: @item[:object_id])
return if !ticket
article = ticket.articles.first
return if !article
# if sender is not customer, do not change anything
sender = Ticket::Article::Sender.lookup(id: article.sender_id)
return if !sender
return if sender['name'] != 'Customer'
# set email attributes
type = Ticket::Article::Type.lookup(id: article.type_id)
return if type['name'] != 'email'
# add queue job to update current signature of user id
SignatureDetection.rebuild_user(article.created_by_id)
# user
user = User.lookup(id: article.created_by_id)
return if !user
return if !user.preferences
return if !user.preferences[:signature_detection]
article.preferences[:signature_detection] = SignatureDetection.find_signature_line(user.preferences[:signature_detection], article.body)
article.save
end
end

View file

@ -6,14 +6,14 @@ class Transaction::Slack
backend = Transaction::Slack.new( backend = Transaction::Slack.new(
object: 'Ticket', object: 'Ticket',
type: 'create', type: 'create',
ticket_id: 1, object_id: 1,
) )
backend.perform backend.perform
{ {
object: 'Ticket', object: 'Ticket',
type: 'update', type: 'update',
ticket_id: 123, object_id: 123,
via_web: true, via_web: true,
changes: { changes: {
'attribute1' => [before, now], 'attribute1' => [before, now],
@ -27,6 +27,10 @@ backend.perform
end end
def perform def perform
# return if we run import mode
return if Setting.get('import_mode')
return if @item[:object] != 'Ticket' return if @item[:object] != 'Ticket'
return if !Setting.get('slack_integration') return if !Setting.get('slack_integration')
@ -34,7 +38,7 @@ backend.perform
return if !config return if !config
return if !config['items'] return if !config['items']
ticket = Ticket.find(@item[:ticket_id]) ticket = Ticket.find(@item[:object_id])
if @item[:article_id] if @item[:article_id]
article = Ticket::Article.find(@item[:article_id]) article = Ticket::Article.find(@item[:article_id])
end end

View file

@ -30,7 +30,6 @@ module Zammad
'observer::_ticket::_article::_communicate_email', 'observer::_ticket::_article::_communicate_email',
'observer::_ticket::_article::_communicate_facebook', 'observer::_ticket::_article::_communicate_facebook',
'observer::_ticket::_article::_communicate_twitter', 'observer::_ticket::_article::_communicate_twitter',
'observer::_ticket::_article::_signature_detection',
'observer::_ticket::_reset_new_state', 'observer::_ticket::_reset_new_state',
'observer::_ticket::_escalation_calculation', 'observer::_ticket::_escalation_calculation',
'observer::_ticket::_ref_object_touch', 'observer::_ticket::_ref_object_touch',

View file

@ -0,0 +1,13 @@
class UpdateSignatureDetection < ActiveRecord::Migration
def up
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '1000_signature_detection',
area: 'Transaction::Backend',
description: 'Define the transaction backend to detect customers signature in email.',
options: {},
state: 'Transaction::SignatureDetection',
frontend: false
)
end
end

View file

@ -1778,6 +1778,15 @@ Setting.create_if_not_exists(
state: 'Transaction::Notification', state: 'Transaction::Notification',
frontend: false frontend: false
) )
Setting.create_if_not_exists(
title: 'Define transaction backend.',
name: '1000_signature_detection',
area: 'Transaction::Backend',
description: 'Define the transaction backend to detect customers signature in email.',
options: {},
state: 'Transaction::SignatureDetection',
frontend: false
)
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Define transaction backend.', title: 'Define transaction backend.',
name: '6000_slack_webhook', name: '6000_slack_webhook',

View file

@ -131,7 +131,6 @@ returns
=end =end
def self.by_user_id(user_id) def self.by_user_id(user_id)
type = Ticket::Article::Type.lookup(name: 'email') type = Ticket::Article::Type.lookup(name: 'email')
sender = Ticket::Article::Sender.lookup(name: 'Customer') sender = Ticket::Article::Sender.lookup(name: 'Customer')
article_bodies = [] article_bodies = []
@ -142,7 +141,7 @@ returns
article_bodies.push article.body article_bodies.push article.body
} }
find_signature( article_bodies ) find_signature(article_bodies)
end end
=begin =begin
@ -167,7 +166,7 @@ returns
=begin =begin
rebuild signature for user rebuild signature detection for user
SignatureDetection.rebuild_user(user_id) SignatureDetection.rebuild_user(user_id)

View file

@ -73,6 +73,7 @@ class EmailSignaturDetectionTest < ActiveSupport::TestCase
ticket1, article1, user1, mail = Channel::EmailParser.new.process({}, raw_email) ticket1, article1, user1, mail = Channel::EmailParser.new.process({}, raw_email)
assert(ticket1) assert(ticket1)
assert(article1) assert(article1)
Delayed::Worker.new.work_off
# process email II # process email II
file = File.open("#{Rails.root}/test/fixtures/email_signature_detection/client_a_2.txt", 'rb') file = File.open("#{Rails.root}/test/fixtures/email_signature_detection/client_a_2.txt", 'rb')
@ -80,8 +81,6 @@ class EmailSignaturDetectionTest < ActiveSupport::TestCase
ticket2, article2, user2, mail = Channel::EmailParser.new.process({}, raw_email) ticket2, article2, user2, mail = Channel::EmailParser.new.process({}, raw_email)
assert(ticket2) assert(ticket2)
assert(article2) assert(article2)
# process background jobs (user signature detection & article signature detection)
Delayed::Worker.new.work_off Delayed::Worker.new.work_off
# check if user2 has a signature_detection value # check if user2 has a signature_detection value
@ -94,8 +93,10 @@ class EmailSignaturDetectionTest < ActiveSupport::TestCase
ticket3, article3, user3, mail = Channel::EmailParser.new.process({}, raw_email) ticket3, article3, user3, mail = Channel::EmailParser.new.process({}, raw_email)
assert(ticket3) assert(ticket3)
assert(article3) assert(article3)
Delayed::Worker.new.work_off
# check if article3 has a signature_detection value # check if article3 has a signature_detection value
article3 = Ticket::Article.find(article3.id)
assert_equal(article3.preferences[:signature_detection], 6) assert_equal(article3.preferences[:signature_detection], 6)
# relbuild all # relbuild all

View file

@ -904,11 +904,11 @@ class TicketNotificationTest < ActiveSupport::TestCase
list = EventBuffer.list('transaction') list = EventBuffer.list('transaction')
list_objects = Observer::Transaction.get_uniq_changes(list) list_objects = Observer::Transaction.get_uniq_changes(list)
assert_equal('some notification event test 1', list_objects[ticket1.id][:changes]['title'][0]) assert_equal('some notification event test 1', list_objects['Ticket'][ticket1.id][:changes]['title'][0])
assert_equal('some notification event test 1 - #2', list_objects[ticket1.id][:changes]['title'][1]) assert_equal('some notification event test 1 - #2', list_objects['Ticket'][ticket1.id][:changes]['title'][1])
assert_not(list_objects[ticket1.id][:changes]['priority']) assert_not(list_objects['Ticket'][ticket1.id][:changes]['priority'])
assert_equal(2, list_objects[ticket1.id][:changes]['priority_id'][0]) assert_equal(2, list_objects['Ticket'][ticket1.id][:changes]['priority_id'][0])
assert_equal(3, list_objects[ticket1.id][:changes]['priority_id'][1]) assert_equal(3, list_objects['Ticket'][ticket1.id][:changes]['priority_id'][1])
# update ticket attributes # update ticket attributes
ticket1.title = "#{ticket1.title} - #3" ticket1.title = "#{ticket1.title} - #3"
@ -918,11 +918,11 @@ class TicketNotificationTest < ActiveSupport::TestCase
list = EventBuffer.list('transaction') list = EventBuffer.list('transaction')
list_objects = Observer::Transaction.get_uniq_changes(list) list_objects = Observer::Transaction.get_uniq_changes(list)
assert_equal('some notification event test 1', list_objects[ticket1.id][:changes]['title'][0]) assert_equal('some notification event test 1', list_objects['Ticket'][ticket1.id][:changes]['title'][0])
assert_equal('some notification event test 1 - #2 - #3', list_objects[ticket1.id][:changes]['title'][1]) assert_equal('some notification event test 1 - #2 - #3', list_objects['Ticket'][ticket1.id][:changes]['title'][1])
assert_not(list_objects[ticket1.id][:changes]['priority']) assert_not(list_objects['Ticket'][ticket1.id][:changes]['priority'])
assert_equal(2, list_objects[ticket1.id][:changes]['priority_id'][0]) assert_equal(2, list_objects['Ticket'][ticket1.id][:changes]['priority_id'][0])
assert_equal(1, list_objects[ticket1.id][:changes]['priority_id'][1]) assert_equal(1, list_objects['Ticket'][ticket1.id][:changes]['priority_id'][1])
end end