Class: Embulk::Input::Lkqd

Inherits:
InputPlugin
  • Object
show all
Defined in:
lib/embulk/input/lkqd.rb

Constant Summary collapse

DEFAULT_COLUMNS =
{
  # dimensions' columns
  'Time'=> { 'type' => 'timestamp', 'format' => "%Y-%m-%dT%H" },
  'Account'=> { 'type' => 'string' },
  'Supply Source ID'=> { 'type' => 'string' },
  'Supply Source'=> { 'type' => 'string' },
  'Supply Partner'=> { 'type' => 'string' },
  'Environment'=> { 'type' => 'string' },
  'Domain'=> { 'type' => 'string' },
  'App Name'=> { 'type' => 'string' },
  'Bundle ID'=> { 'type' => 'string' },
  'Supply Tag Type'=> { 'type' => 'string' },
  'Demand Partner'=> { 'type' => 'string' },
  'Demand Deal ID' => { 'type' => 'string' },
  'Demand Deal'=> { 'type' => 'string' },
  'Demand Tag'=> { 'type' => 'string' },
  'Format'=> { 'type' => 'string' },
  'Country'=> { 'type' => 'string' },
  'Device Type'=> { 'type' => 'string' },
  'OS'=> { 'type' => 'string' },
  'Width X Height'=> { 'type' => 'string' },
  # "Opportunity report" columns
  'Tag Loads' => { 'type' => 'long' },
  'Opportunities' => { 'type' => 'long' },
  'Format Loads' => { 'type' => 'long' },
  'Format Fill Rate' => { 'type' => 'double' },
  'Ineligible Ops: Demand' => { 'type' => 'long' },
  'Ineligible Ops: Restrictions' => { 'type' => 'long' },
  'Impressions' => { 'type' => 'long' },
  'Fill Rate' => { 'type' => 'double' },
  'Efficiency Rate' => { 'type' => 'double' },
  'CPM' => { 'type' => 'double' },
  'Revenue' => { 'type' => 'double' },
  'Cost' => { 'type' => 'double' },
  'Profit' => { 'type' => 'double' },
  'Profit Margin' => { 'type' => 'double' },
  'Clicks' => { 'type' => 'long' },
  'CTR' => { 'type' => 'double' },
  'Ad Starts' => { 'type' => 'long' },
  'Ad Starts Rate' => { 'type' => 'double' },
  '25% Views' => { 'type' => 'long' },
  '50% Views' => { 'type' => 'long' },
  '75% Views' => { 'type' => 'long' },
  '100% Views' => { 'type' => 'long' },
  '25% View Rate' => { 'type' => 'double' },
  '50% View Rate' => { 'type' => 'double' },
  '75% View Rate' => { 'type' => 'double' },
  '100% View Rate' => { 'type' => 'double' },
  'Viewability Measured Rate' => { 'type' => 'double' },
  'Viewability Rate' => { 'type' => 'double' },
  # "Request report" columns
  'Tag Requests' => { 'type' => 'long' },
  'Ads' => { 'type' => 'long' },
  'VAST Ads' => { 'type' => 'long' },
  'VPAID Ads' => { 'type' => 'long' },
  'Wins' => { 'type' => 'long' },
  'Ad Rate' => { 'type' => 'double' },
  'VAST Ad Rate' => { 'type' => 'double' },
  'VPAID Ad Rate' => { 'type' => 'double' },
  'Win Rate' => { 'type' => 'double' },
  'VPAID Responses' => { 'type' => 'long' },
  'VPAID Attempts' => { 'type' => 'long' },
  'VPAID Successes' => { 'type' => 'long' },
  'VPAID Opt Outs' => { 'type' => 'long' },
  'VPAID Timeouts' => { 'type' => 'long' },
  'VPAID Errors' => { 'type' => 'long' },
  'VPAID Success Rate' => { 'type' => 'double' },
  'VPAID Opt Out Rate' => { 'type' => 'double' },
  'VPAID Timeout Rate' => { 'type' => 'double' },
  'VPAID Error Rate' => { 'type' => 'double' },
  'Tag Timeouts' => { 'type' => 'long' },
  'Tag Timeout Rate' => { 'type' => 'double' },
  'Tag Errors' => { 'type' => 'long' },
  'Tag Error Rate' => { 'type' => 'long' },
  'Playback Errors' => { 'type' => 'long' },
  'Playback Error Rate' => { 'type' => 'double' },
  # custom data columns
  'Custom 1'=> { 'type' => 'string' },
  'Custom 2'=> { 'type' => 'string' },
  'Custom 3'=> { 'type' => 'string' },
  'Custom 4'=> { 'type' => 'string' },
  'Custom 5'=> { 'type' => 'string' },
  'Custom 6'=> { 'type' => 'string' },
  'Custom 7'=> { 'type' => 'string' },
  'Custom 8'=> { 'type' => 'string' },
  'Custom 9'=> { 'type' => 'string' },
  'Custom 10'=> { 'type' => 'string' },
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.guess(config) ⇒ Object



69
70
71
# File 'lib/embulk/input/lkqd.rb', line 69

def self.guess(config)
  return {}
end

.guess_column_type(metrics, column_name) ⇒ Object



77
78
79
80
81
82
83
84
85
# File 'lib/embulk/input/lkqd.rb', line 77

def self.guess_column_type(metrics, column_name)
  column_name.gsub!(/^\W/, '')
  column_option = DEFAULT_COLUMNS[column_name]
  if column_option
    return {type: column_option['type'].to_sym, name: column_name, format: column_option['format']}
  else
    return {type: :string, name: column_name}
  end
end

.measurable_impressions?(task) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
111
# File 'lib/embulk/input/lkqd.rb', line 108

def self.measurable_impressions?(task)
  task['try_convert'] && task['measurable_impressions'] &&
  task['viewability_measured_rate_index'] && task['impressions_index']
end

.request_lkqd(config) ⇒ Object



73
74
75
# File 'lib/embulk/input/lkqd.rb', line 73

def self.request_lkqd(config)
  ::HTTP.auth("Basic #{config[:authorization]}").post(config[:endpoint], json: config[:report_parameters])
end

.resume(task, columns, count, &control) ⇒ Object



62
63
64
65
66
67
# File 'lib/embulk/input/lkqd.rb', line 62

def self.resume(task, columns, count, &control)
  task_reports = yield(task, columns, count)

  next_config_diff = {}
  return next_config_diff
end

.transaction(config, &control) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/embulk/input/lkqd.rb', line 14

def self.transaction(config, &control)
  task = {
    "secret_key_id" => config.param("secret_key_id", :string),
    "secret_key" => config.param("secret_key", :string),
    "endpoint" => config.param("endpoint", :string, default: 'https://api.lkqd.com/reports'),
    "copy_temp_to" => config.param("copy_temp_to", :string, default: nil),
    "try_convert" => config.param("try_convert", :bool, default: true),
    "measurable_impressions" => config.param("measurable_impressions", :bool, default: false),
    "viewable_impressions" => config.param("viewable_impressions", :bool, default: false),
    "report_parameters" => config.param("report_parameters", :hash, default: {}),
  }
  task['authorization'] = Base64.urlsafe_encode64("#{task['secret_key_id']}:#{task['secret_key']}")

  response = request_lkqd({authorization: task['authorization'], endpoint: task['endpoint'], report_parameters: task['report_parameters']})
  tempfile = Tempfile.new('emublk-input-lkqd_')
  while chunk = response.body.readpartial
    tempfile.write chunk
  end
  tempfile.close
  task['tempfile_path'] = tempfile.path

  FileUtils.cp(task['tempfile_path'], task['copy_temp_to']) if task['copy_temp_to']

  columns = []
  ::CSV.foreach(tempfile.path).first.each_with_index do |column_name, index|
    column_type = guess_column_type(task['report_parameters']['metrics'], column_name)
    column = Column.new({index: index}.merge(column_type))
    if column.name == 'Viewability Measured Rate'
      task['viewability_measured_rate_index'] = index
    elsif column.name == 'Viewability Rate'
      task['viewability_rate_index'] = index
    elsif column.name == 'Impressions'
      task['impressions_index'] = index
    end
    columns << column
  end

  if measurable_impressions?(task)
    columns << Column.new({index: columns.size, type: :double, name: 'Measurable Impressions'})
  end

  if viewable_impressions?(task)
    columns << Column.new({index: columns.size, type: :double, name: 'Viewable Impressions'})
  end

  resume(task, columns, 1, &control)
end

.try_convert(row, options = {}) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/embulk/input/lkqd.rb', line 87

def self.try_convert(row, options={})
  return row.map do |field|
    name, value = field
    column_name = name.gsub(/^\W/, '')
    column_option = DEFAULT_COLUMNS[column_name]
    if column_option.nil?
      next value
    elsif column_option['type'] == 'timestamp'
      next Time.strptime(value + " " + options[:timezone], column_option['format'] + " %Z").to_i
    elsif column_option['type'] == 'long'
      next value.gsub(',','').to_i
    elsif column_option['type'] == 'double' && value.match(/%$/)
      next value.gsub(',','').to_f / 100.0
    elsif column_option['type'] == 'double'
      next value.gsub(',','').to_f
    else
      next value
    end
  end
end

.viewable_impressions?(task) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
116
# File 'lib/embulk/input/lkqd.rb', line 113

def self.viewable_impressions?(task)
  task['try_convert'] && task['viewable_impressions'] &&
  task['viewability_rate_index'] && task['viewability_measured_rate_index'] && task['impressions_index']
end

Instance Method Details

#initObject



118
119
120
# File 'lib/embulk/input/lkqd.rb', line 118

def init
  @task = task
end

#runObject



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/embulk/input/lkqd.rb', line 122

def run
  convert_options = {timezone: @task['report_parameters']['timezone']}
  try_convert = @task['try_convert']
  viewability_measured_rate_index = @task['viewability_measured_rate_index']
  viewability_rate_index = @task['viewability_rate_index']
  impressions_index = @task['impressions_index']

  CSV.foreach(@task['tempfile_path'], {headers: true}).each do |row|
    row = try_convert ? Lkqd.try_convert(row, convert_options) : row
    if Lkqd.measurable_impressions?(@task)
      row << row[impressions_index] * row[viewability_measured_rate_index]
    end

    if Lkqd.viewable_impressions?(@task)
      row << row[impressions_index] * row[viewability_measured_rate_index] * row[viewability_rate_index]
    end
    page_builder.add(row)
  end
  page_builder.finish
  FileUtils.rm_rf(@task['tempfile_path'])

  task_report = {}
  return task_report
end