123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968 |
- module Import
- end
- module Import::OTRS2
- =begin
- result = request_json('Subaction=List', 1)
- return
- { some json structure }
- result = request_json('Subaction=List')
- return
- "some data string"
- =end
- def self.request_json(part, data_only = false)
- response = request(part)
- if !response
- raise "Can't connect to Zammad Migrator"
- end
- if !response.success?
- raise "Can't connect to Zammad Migrator"
- end
- result = json(response)
- if !result
- raise "Invalid response"
- end
- if data_only
- result['Result']
- else
- result
- end
- end
- =begin
- start get request to backend, add auth data automatically
- result = request('Subaction=List')
- return
- "some data string"
- =end
- def self.request(part)
- url = Setting.get('import_otrs_endpoint') + part + ';Key=' + Setting.get('import_otrs_endpoint_key')
- puts 'GET: ' + url
- response = UserAgent.request(
- url,
- {
- :user => Setting.get('import_otrs_user'),
- :password => Setting.get('import_otrs_password'),
- },
- )
- if !response.success?
- puts "ERROR: #{response.error}"
- return
- end
- response
- end
- =begin
- start post request to backend, add auth data automatically
- result = request('Subaction=List')
- return
- "some data string"
- =end
- def self.post(data)
- url = Setting.get('import_otrs_endpoint')
- data['Key'] = Setting.get('import_otrs_endpoint_key')
- puts 'POST: ' + url
- response = UserAgent.request(
- url,
- {
- :method => 'post',
- :data => data,
- :user => Setting.get('import_otrs_user'),
- :password => Setting.get('import_otrs_password'),
- },
- )
- if !response.success?
- puts "ERROR: #{response.error}"
- return
- end
- response
- end
- =begin
- start post request to backend, add auth data automatically
- result = json('some response string')
- return
- {}
- =end
- def self.json(response)
- data = Encode.conv( 'utf8', response.body.to_s )
- JSON.parse( data )
- end
- def self.load( object, limit = '', offset = '' )
- request_json( ";Subaction=Export;Object=#{object};Limit=#{limit};Offset=#{offset}", 1 )
- end
- =begin
- start get request to backend to check connection
- result = connection_test
- return
- true | false
- =end
- def self.connection_test
- return self.request_json('')
- end
- =begin
- get object statisitic from server ans save it in cache
- result = get_statisitic('Subaction=List')
- return
- {
- 'Ticket' => 1234,
- 'User' => 123,
- 'SomeObject' => 999,
- }
- =end
- def self.get_statisitic
- # check cache
- cache = Cache.get('import_otrs_stats')
- if cache
- return cache
- end
- # retrive statistic
- statistic = self.request_json(';Subaction=List', 1)
- if statistic
- Cache.write('import_otrs_stats', statistic)
- end
- statistic
- end
- =begin
- return current import state
- result = get_current_state
- return
- {
- :Ticket => {
- :total => 1234,
- :done => 13,
- },
- :Base => {
- :total => 1234,
- :done => 13,
- },
- }
- =end
- def self.get_current_state
- data = self.get_statisitic
- base = Group.count + Ticket::State.count + Ticket::Priority.count
- base_total = data['Queue'] + data['State'] + data['Priority']
- user = User.count
- user_total = data['User'] + data['CustomerUser']
- data = {
- :Base => {
- :done => base,
- :total => base_total || 0,
- },
- :User => {
- :done => user,
- :total => user_total || 0,
- },
- :Ticket => {
- :done => Ticket.count,
- :total => data['Ticket'] || 0,
- },
- }
- data
- end
- #
- # start import
- #
- # Import::OTRS2.start
- #
- def self.start
- puts 'Start import...'
- # check if system is in import mode
- if !Setting.get('import_mode')
- raise "System is not in import mode!"
- end
- result = request_json('')
- if !result['Success']
- "API key not valid!"
- end
- # create states
- records = load('State')
- state(records)
- # create priorities
- records = load('Priority')
- priority(records)
- # create groups
- records = load('Queue')
- ticket_group(records)
- # create agents
- records = load('User')
- user(records)
- # create customers
- count = 0
- steps = 30
- run = true
- while run
- count += steps
- records = load('CustomerUser', steps, count-steps)
- if !records || !records[0]
- puts "all customers imported."
- run = false
- next
- end
- customer(records)
- end
- Thread.abort_on_exception = true
- thread_count = 8
- threads = {}
- count = 0
- locks = { :User => {} }
- (1..thread_count).each {|thread|
- threads[thread] = Thread.new {
- sleep thread * 3
- puts "Started import thread# #{thread} ..."
- run = true
- steps = 20
- while run
- count += steps
- sleep 1
- puts "loading... thread# #{thread} ..."
- offset = count-steps
- if offset != 0
- offset = count - steps + 1
- end
- records = load( 'Ticket', steps, count-steps )
- if !records || !records[0]
- puts "... thread# #{thread}, no more work."
- run = false
- next
- end
- _ticket_result(records, locks)
- end
- }
- }
- (1..thread_count).each {|thread|
- threads[thread].join
- }
- Setting.set( 'system_init_done', true )
- #Setting.set( 'import_mode', false )
- true
- end
- def self.diff_worker
- return if !Setting.get('import_mode')
- return if Setting.get('import_otrs_endpoint') == 'http://otrs_host/otrs'
- self.diff
- end
- def self.diff
- puts 'Start diff...'
- # check if system is in import mode
- if !Setting.get('import_mode')
- raise "System is not in import mode!"
- end
- # create states
- state
- # create priorities
- priority
- # create groups
- ticket_group
- # create agents
- user
- self.ticket_diff
- return
- end
- def self.ticket_diff()
- url = "public.pl?Action=Export;Type=TicketDiff;Limit=30"
- response = request( url )
- return if !response
- return if !response.success?
- result = json(response)
- self._ticket_result(result)
- end
- def self.ticket(ticket_ids)
- url = "public.pl?Action=Export;Type=Ticket;"
- ticket_ids.each {|ticket_id|
- url = url + "TicketID=#{CGI::escape ticket_id};"
- }
- response = request( url )
- return if !response
- return if !response.success?
- result = json(response)
- self._ticket_result(result)
- end
- def self._ticket_result(result, locks)
- # puts result.inspect
- map = {
- :Ticket => {
- :Changed => :updated_at,
- :Created => :created_at,
- :CreateBy => :created_by_id,
- :TicketNumber => :number,
- :QueueID => :group_id,
- :StateID => :state_id,
- :PriorityID => :priority_id,
- :Owner => :owner,
- :CustomerUserID => :customer,
- :Title => :title,
- :TicketID => :id,
- :FirstResponse => :first_response,
- # :FirstResponseTimeDestinationDate => :first_response_escal_date,
- # :FirstResponseInMin => :first_response_in_min,
- # :FirstResponseDiffInMin => :first_response_diff_in_min,
- :Closed => :close_time,
- # :SoltutionTimeDestinationDate => :close_time_escal_date,
- # :CloseTimeInMin => :close_time_in_min,
- # :CloseTimeDiffInMin => :close_time_diff_in_min,
- },
- :Article => {
- :SenderType => :sender,
- :ArticleType => :type,
- :TicketID => :ticket_id,
- :ArticleID => :id,
- :Body => :body,
- :From => :from,
- :To => :to,
- :Cc => :cc,
- :Subject => :subject,
- :InReplyTo => :in_reply_to,
- :MessageID => :message_id,
- # :ReplyTo => :reply_to,
- :References => :references,
- :Changed => :updated_at,
- :Created => :created_at,
- :ChangedBy => :updated_by_id,
- :CreatedBy => :created_by_id,
- },
- }
- result.each {|record|
- ticket_new = {
- :title => '',
- :created_by_id => 1,
- :updated_by_id => 1,
- }
- map[:Ticket].each { |key,value|
- if record[key.to_s] && record[key.to_s].class == String
- ticket_new[value] = Encode.conv( 'utf8', record[key.to_s] )
- else
- ticket_new[value] = record[key.to_s]
- end
- }
- ticket_old = Ticket.where( :id => ticket_new[:id] ).first
- # find owner
- if ticket_new[:owner]
- user = User.lookup( :login => ticket_new[:owner] )
- if user
- ticket_new[:owner_id] = user.id
- else
- ticket_new[:owner_id] = 1
- end
- ticket_new.delete(:owner)
- end
- # find customer
- if ticket_new[:customer]
- user = User.lookup( :login => ticket_new[:customer] )
- if user
- ticket_new[:customer_id] = user.id
- else
- ticket_new[:customer_id] = 1
- end
- ticket_new.delete(:customer)
- else
- ticket_new[:customer_id] = 1
- end
- # set state types
- if ticket_old
- puts "update Ticket.find(#{ticket_new[:id]})"
- ticket_old.update_attributes(ticket_new)
- else
- puts "add Ticket.find(#{ticket_new[:id]})"
- ticket = Ticket.new(ticket_new)
- ticket.id = ticket_new[:id]
- ticket.save
- end
- record['Articles'].each { |article|
- # get article values
- article_new = {
- :created_by_id => 1,
- :updated_by_id => 1,
- }
- map[:Article].each { |key,value|
- if article[key.to_s]
- article_new[value] = Encode.conv( 'utf8', article[key.to_s] )
- end
- }
- # create customer/sender if needed
- if article_new[:sender] == 'customer' && article_new[:created_by_id].to_i == 1 && !article_new[:from].empty?
- email = nil
- begin
- email = Mail::Address.new( article_new[:from] ).address
- rescue
- email = article_new[:from]
- if article_new[:from] =~ /<(.+?)>/
- email = $1
- end
- end
- # create article user if not exists
- while locks[:User][ email ]
- puts "user #{email} is locked"
- sleep 1
- end
- # lock user
- locks[:User][ email ] = true
- user = User.where( :email => email ).first
- if !user
- user = User.where( :login => email ).first
- end
- if !user
- begin
- display_name = Mail::Address.new( article_new[:from] ).display_name ||
- ( Mail::Address.new( article_new[:from] ).comments && Mail::Address.new( article_new[:from] ).comments[0] )
- rescue
- display_name = article_new[:from]
- end
- # do extra decoding because we needed to use field.value
- display_name = Mail::Field.new( 'X-From', display_name ).to_s
- roles = Role.lookup( :name => 'Customer' )
- user = User.create(
- :login => email,
- :firstname => display_name,
- :lastname => '',
- :email => email,
- :password => '',
- :active => true,
- :role_ids => [roles.id],
- :updated_by_id => 1,
- :created_by_id => 1,
- )
- end
- article_new[:created_by_id] = user.id
- # unlock user
- locks[:User][ email ] = false
- end
- if article_new[:sender] == 'customer'
- article_new[:sender_id] = Ticket::Article::Sender.lookup( :name => 'Customer' ).id
- article_new.delete( :sender )
- end
- if article_new[:sender] == 'agent'
- article_new[:sender_id] = Ticket::Article::Sender.lookup( :name => 'Agent' ).id
- article_new.delete( :sender )
- end
- if article_new[:sender] == 'system'
- article_new[:sender_id] = Ticket::Article::Sender.lookup( :name => 'System' ).id
- article_new.delete( :sender )
- end
- if article_new[:type] == 'email-external'
- article_new[:type_id] = Ticket::Article::Type.lookup( :name => 'email' ).id
- article_new[:internal] = false
- elsif article_new[:type] == 'email-internal'
- article_new[:type_id] = Ticket::Article::Type.lookup( :name => 'email' ).id
- article_new[:internal] = true
- elsif article_new[:type] == 'note-external'
- article_new[:type_id] = Ticket::Article::Type.lookup( :name => 'note' ).id
- article_new[:internal] = false
- elsif article_new[:type] == 'note-internal'
- article_new[:type_id] = Ticket::Article::Type.lookup( :name => 'note' ).id
- article_new[:internal] = true
- elsif article_new[:type] == 'phone'
- article_new[:type_id] = Ticket::Article::Type.lookup( :name => 'phone' ).id
- article_new[:internal] = false
- elsif article_new[:type] == 'webrequest'
- article_new[:type_id] = Ticket::Article::Type.lookup( :name => 'web' ).id
- article_new[:internal] = false
- else
- article_new[:type_id] = 9
- end
- article_new.delete( :type )
- article_old = Ticket::Article.where( :id => article_new[:id] ).first
- # set state types
- if article_old
- puts "update Ticket::Article.find(#{article_new[:id]})"
- article_old.update_attributes(article_new)
- else
- puts "add Ticket::Article.find(#{article_new[:id]})"
- article = Ticket::Article.new(article_new)
- article.id = article_new[:id]
- article.save
- end
- }
- record['History'].each { |history|
- if history['HistoryType'] == 'NewTicket'
- History.add(
- :id => history['HistoryID'],
- :o_id => history['TicketID'],
- :history_type => 'created',
- :history_object => 'Ticket',
- :created_at => history['CreateTime'],
- :created_by_id => history['CreateBy']
- )
- end
- if history['HistoryType'] == 'StateUpdate'
- data = history['Name']
- # "%%new%%open%%"
- from = nil
- to = nil
- if data =~ /%%(.+?)%%(.+?)%%/
- from = $1
- to = $2
- state_from = Ticket::State.lookup( :name => from )
- state_to = Ticket::State.lookup( :name => to )
- if state_from
- from_id = state_from.id
- end
- if state_to
- to_id = state_to.id
- end
- end
- History.add(
- :id => history['HistoryID'],
- :o_id => history['TicketID'],
- :history_type => 'updated',
- :history_object => 'Ticket',
- :history_attribute => 'state',
- :value_from => from,
- :id_from => from_id,
- :value_to => to,
- :id_to => to_id,
- :created_at => history['CreateTime'],
- :created_by_id => history['CreateBy']
- )
- end
- if history['HistoryType'] == 'Move'
- data = history['Name']
- # "%%Queue1%%5%%Postmaster%%1"
- from = nil
- to = nil
- if data =~ /%%(.+?)%%(.+?)%%(.+?)%%(.+?)$/
- from = $1
- from_id = $2
- to = $3
- to_id = $4
- end
- History.add(
- :id => history['HistoryID'],
- :o_id => history['TicketID'],
- :history_type => 'updated',
- :history_object => 'Ticket',
- :history_attribute => 'group',
- :value_from => from,
- :value_to => to,
- :id_from => from_id,
- :id_to => to_id,
- :created_at => history['CreateTime'],
- :created_by_id => history['CreateBy']
- )
- end
- if history['HistoryType'] == 'PriorityUpdate'
- data = history['Name']
- # "%%3 normal%%3%%5 very high%%5"
- from = nil
- to = nil
- if data =~ /%%(.+?)%%(.+?)%%(.+?)%%(.+?)$/
- from = $1
- from_id = $2
- to = $3
- to_id = $4
- end
- History.add(
- :id => history['HistoryID'],
- :o_id => history['TicketID'],
- :history_type => 'updated',
- :history_object => 'Ticket',
- :history_attribute => 'priority',
- :value_from => from,
- :value_to => to,
- :id_from => from_id,
- :id_to => to_id,
- :created_at => history['CreateTime'],
- :created_by_id => history['CreateBy']
- )
- end
- if history['ArticleID'] && history['ArticleID'] != 0
- History.add(
- :id => history['HistoryID'],
- :o_id => history['ArticleID'],
- :history_type => 'created',
- :history_object => 'Ticket::Article',
- :related_o_id => history['TicketID'],
- :related_history_object => 'Ticket',
- :created_at => history['CreateTime'],
- :created_by_id => history['CreateBy']
- )
- end
- }
- }
- end
- # sync ticket states
- def self.state(records)
- map = {
- :ChangeTime => :updated_at,
- :CreateTime => :created_at,
- :CreateBy => :created_by_id,
- :ChangeBy => :updated_by_id,
- :Name => :name,
- :ID => :id,
- :ValidID => :active,
- :Comment => :note,
- };
- # rename states to handle not uniq issues
- Ticket::State.all.each {|state|
- state.name = state.name + '_tmp'
- state.save
- }
- records.each { |state|
- _set_valid(state)
- # get new attributes
- state_new = {
- :created_by_id => 1,
- :updated_by_id => 1,
- }
- map.each { |key,value|
- if state[key.to_s]
- state_new[value] = state[key.to_s]
- end
- }
- # check if state already exists
- state_old = Ticket::State.where( :id => state_new[:id] ).first
- # puts 'st: ' + state['TypeName']
- # set state types
- if state['TypeName'] == 'pending auto'
- state['TypeName'] = 'pending action'
- end
- state_type = Ticket::StateType.where( :name => state['TypeName'] ).first
- state_new[:state_type_id] = state_type.id
- if state_old
- # puts 'TS: ' + state_new.inspect
- state_old.update_attributes(state_new)
- else
- state = Ticket::State.new(state_new)
- state.id = state_new[:id]
- state.save
- end
- }
- end
- # sync ticket priorities
- def self.priority(records)
- map = {
- :ChangeTime => :updated_at,
- :CreateTime => :created_at,
- :CreateBy => :created_by_id,
- :ChangeBy => :updated_by_id,
- :Name => :name,
- :ID => :id,
- :ValidID => :active,
- :Comment => :note,
- };
- records.each { |priority|
- _set_valid(priority)
- # get new attributes
- priority_new = {
- :created_by_id => 1,
- :updated_by_id => 1,
- }
- map.each { |key,value|
- if priority[key.to_s]
- priority_new[value] = priority[key.to_s]
- end
- }
- # check if state already exists
- priority_old = Ticket::Priority.where( :id => priority_new[:id] ).first
- # set state types
- if priority_old
- priority_old.update_attributes(priority_new)
- else
- priority = Ticket::Priority.new(priority_new)
- priority.id = priority_new[:id]
- priority.save
- end
- }
- end
- # sync ticket groups / queues
- def self.ticket_group(records)
- map = {
- :ChangeTime => :updated_at,
- :CreateTime => :created_at,
- :CreateBy => :created_by_id,
- :ChangeBy => :updated_by_id,
- :Name => :name,
- :QueueID => :id,
- :ValidID => :active,
- :Comment => :note,
- };
- records.each { |group|
- _set_valid(group)
- # get new attributes
- group_new = {
- :created_by_id => 1,
- :updated_by_id => 1,
- }
- map.each { |key,value|
- if group[key.to_s]
- group_new[value] = group[key.to_s]
- end
- }
- # check if state already exists
- group_old = Group.where( :id => group_new[:id] ).first
- # set state types
- if group_old
- group_old.update_attributes(group_new)
- else
- group = Group.new(group_new)
- group.id = group_new[:id]
- group.save
- end
- }
- end
- # sync agents
- def self.user(records)
- map = {
- :ChangeTime => :updated_at,
- :CreateTime => :created_at,
- :CreateBy => :created_by_id,
- :ChangeBy => :updated_by_id,
- :UserID => :id,
- :ValidID => :active,
- :Comment => :note,
- :UserEmail => :email,
- :UserFirstname => :firstname,
- :UserLastname => :lastname,
- # :UserTitle =>
- :UserLogin => :login,
- :UserPw => :password,
- };
- role = Role.lookup( :name => 'Agent' )
- records.each { |user|
- _set_valid(user)
- # get new attributes
- user_new = {
- :created_by_id => 1,
- :updated_by_id => 1,
- :source => 'OTRS Import',
- :role_ids => [ role.id ],
- }
- map.each { |key,value|
- if user[key.to_s]
- user_new[value] = user[key.to_s]
- end
- }
- if user_new[:password]
- user_new[:password] = "{sha2}#{user_new[:password]}"
- end
- # check if agent already exists
- user_old = User.where( :id => user_new[:id] ).first
- # create / update agent
- if user_old
- puts "update User.find(#{user_old[:id]})"
- # only update roles if different (reduce sql statements)
- if user_old.role_ids == user_new[:role_ids]
- user_new.delete( :role_ids )
- end
- user_old.update_attributes(user_new)
- else
- puts "add User.find(#{user_new[:id]})"
- user = User.new(user_new)
- user.id = user_new[:id]
- user.save
- end
- }
- end
- # sync customers
- def self.customer(records)
- map = {
- :ChangeTime => :updated_at,
- :CreateTime => :created_at,
- :CreateBy => :created_by_id,
- :ChangeBy => :updated_by_id,
- :ValidID => :active,
- :UserComment => :note,
- :UserEmail => :email,
- :UserFirstname => :firstname,
- :UserLastname => :lastname,
- # :UserTitle =>
- :UserLogin => :login,
- :UserPassword => :password,
- :UserPhone => :phone,
- :UserFax => :fax,
- :UserMobile => :mobile,
- :UserStreet => :street,
- :UserZip => :zip,
- :UserCity => :city,
- :UserCountry => :country,
- };
- role_agent = Role.lookup( :name => 'Agent' )
- role_customer = Role.lookup( :name => 'Customer' )
- records.each { |user|
- _set_valid(user)
- # get new attributes
- user_new = {
- :created_by_id => 1,
- :updated_by_id => 1,
- :source => 'OTRS Import',
- :role_ids => [ role_customer.id ],
- }
- map.each { |key,value|
- if user[key.to_s]
- user_new[value] = user[key.to_s]
- end
- }
- # check if customer already exists
- user_old = User.where( :login => user_new[:login] ).first
- # create / update agent
- if user_old
- # do not update user if it is already agent
- if !user_old.role_ids.include?( role_agent.id )
- # only update roles if different (reduce sql statements)
- if user_old.role_ids == user_new[:role_ids]
- user_new.delete( :role_ids )
- end
- puts "update User.find(#{user_old[:id]})"
- user_old.update_attributes(user_new)
- end
- else
- puts "add User.find(#{user_new[:id]})"
- user = User.new(user_new)
- user.save
- end
- }
- end
- # set translate valid ids to active = true|false
- def self._set_valid(record)
- # map
- if record['ValidID'].to_s == '3'
- record['ValidID'] = '2'
- end
- if record['ValidID'].to_s == '2'
- record['ValidID'] = false
- end
- if record['ValidID'].to_s == '1'
- record['ValidID'] = true
- end
- if record['ValidID'].to_s == '0'
- record['ValidID'] = false
- end
- end
- end
|