mixpanel_agent.rb 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. require 'mixpanel_client'
  2. require 'date'
  3. module Agents
  4. class MixpanelAgent < Agent
  5. include WebRequestConcern
  6. cannot_receive_events!
  7. can_dry_run!
  8. default_schedule "every_1d"
  9. DEFAULT_EVENTS_ORDER = [['{{date_published}}', 'time'], ['{{last_updated}}', 'time']]
  10. description do
  11. <<-MD
  12. The Mixpanel Agent checks for analytics data and returns an event.
  13. # Ordering Events
  14. #{description_events_order}
  15. In this Agent, the default value for `events_order` is `#{DEFAULT_EVENTS_ORDER.to_json}`.
  16. MD
  17. end
  18. def default_options
  19. {
  20. 'event_name' => "Page Visit",
  21. 'property' => "Page",
  22. 'value' => "home",
  23. 'time' => 24,
  24. 'interval' => 'hour'
  25. }
  26. end
  27. event_description <<-MD
  28. Events look like:
  29. {
  30. "count": "45",
  31. 'event_name': "Page Visit",
  32. 'property': "Page",
  33. 'value': "home",
  34. 'time': 24,
  35. 'interval': 'hour'
  36. }
  37. MD
  38. def working?
  39. end
  40. def validate_options
  41. errors.add(:base, "event_name is required") unless options['event_name'].present?
  42. unless options['property'].present? && options['value'].present?
  43. errors.add(:base, "Please provide 'property' and 'value'")
  44. end
  45. validate_web_request_options!
  46. validate_events_order
  47. end
  48. def events_order
  49. super.presence || DEFAULT_EVENTS_ORDER
  50. end
  51. def check
  52. create_event :payload => {
  53. count: mixpanel_event_number(options),
  54. event_name: options['event_name'],
  55. property: options['property'],
  56. value: options['value'],
  57. time: options['time'],
  58. interval: options['interval']
  59. }
  60. end
  61. protected
  62. def mixpanel_config
  63. {
  64. api_key: ENV['MIXPANEL_API_KEY'],
  65. api_secret: ENV['MIXPANEL_SECRET_KEY']
  66. }
  67. end
  68. def mixpanel_client
  69. @mixpanel_client ||= Mixpanel::Client.new(mixpanel_config())
  70. end
  71. def mixpanel_event_number(options)
  72. property, value = options[:property], options[:value]
  73. unless (property && value) || (!property && !value)
  74. raise "Must specify both 'property' and 'value' or none"
  75. end
  76. if [TrueClass, FalseClass].include?(value.class)
  77. raise "As of Aug 7, 2013, MixPanel has a bug with querying boolean values\nPlease use number_for_event_using_export until that's fixed"
  78. end
  79. event_name = options[:event_name]
  80. unless event_name
  81. raise "Event name must be provided"
  82. end
  83. type = options[:type] || "general" #MixPanel API uses the term 'general' to mean 'total'
  84. unless ["unique", "general", "average"].include? type
  85. raise "Invalid type #{type}"
  86. end
  87. num_days = options[:time] || 24
  88. interval = options[:interval] || "hour"
  89. mixpanel_options = {
  90. type: type,
  91. unit: interval,
  92. interval: num_days,
  93. limit: 5,
  94. }
  95. if property && value
  96. mixpanel_endpoint = "events/properties/"
  97. mixpanel_options.merge!({
  98. event: event_name,
  99. values: [value],
  100. name: property
  101. })
  102. else
  103. mixpanel_endpoint = "events/"
  104. mixpanel_options.merge!({
  105. event: [event_name]
  106. })
  107. end
  108. data = mixpanel_client.request(mixpanel_endpoint, mixpanel_options)
  109. total_for_events(data)
  110. end
  111. def total_for_events(data)
  112. counts_per_property = data["data"]["values"].collect do |c, values|
  113. values.collect { |k, v| v }.inject(:+)
  114. end
  115. #now, calculate grand total
  116. counts_per_property.inject(:+)
  117. end
  118. ###########################
  119. def number_for_event_using_export(event_name, property, value, num_days = 30)
  120. # TODO:
  121. # MixPanel doesn't understand boolean values for properties
  122. # There is an open ticket, but for now, there is a work around to use export API
  123. # https://mixpanel.com/docs/api-documentation/exporting-raw-data-you-inserted-into-mixpanel
  124. to_date = Date.today
  125. from_date = to_date - num_days
  126. data = mixpanel_client.request('export', {
  127. event: [event_name],
  128. from_date: from_date.to_s,
  129. to_date: to_date.to_s,
  130. where: "boolean(properties[\"#{property}\"]) == #{value} ",
  131. })
  132. data.count
  133. end
  134. end
  135. end