mixpanel_agent.rb 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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 how many times that event was triggerd.
  13. Specify the **API Key** and the **Secret Key** for the mixpanel project that you are querying. If you want to filter events using property, it is necessary to specify the value. The **time** property is an *integer* and the interval can be ```minute```, ```day```, ```month```, ```year```.
  14. MD
  15. end
  16. def default_options
  17. {
  18. 'api_key' => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  19. 'secret_key' => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  20. 'event_name' => "Page Visit",
  21. 'time' => 24,
  22. 'interval' => 'hour',
  23. 'property' => "Page",
  24. 'value' => "home"
  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. if options['property'].present? || options['value'].present?
  43. unless options['property'].present? && options['value'].present?
  44. errors.add(:base, "Please provide 'property' and 'value'")
  45. end
  46. end
  47. validate_web_request_options!
  48. validate_events_order
  49. end
  50. def events_order
  51. super.presence || DEFAULT_EVENTS_ORDER
  52. end
  53. def check
  54. create_event :payload => {
  55. count: mixpanel_event_number(options),
  56. event_name: options['event_name'],
  57. property: options['property'],
  58. value: options['value'],
  59. time: options['time'],
  60. interval: options['interval']
  61. }
  62. end
  63. protected
  64. def mixpanel_config
  65. {
  66. api_key: options['api_key'],
  67. api_secret: options['secret_key']
  68. }
  69. end
  70. def mixpanel_client
  71. @mixpanel_client ||= Mixpanel::Client.new(mixpanel_config())
  72. end
  73. def mixpanel_event_number(options)
  74. property, value = options[:property], options[:value]
  75. unless (property && value) || (!property && !value)
  76. raise "Must specify both 'property' and 'value' or none"
  77. end
  78. if [TrueClass, FalseClass].include?(value.class)
  79. 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"
  80. end
  81. event_name = options[:event_name]
  82. unless event_name
  83. raise "Event name must be provided"
  84. end
  85. type = options[:type] || "general" #MixPanel API uses the term 'general' to mean 'total'
  86. unless ["unique", "general", "average"].include? type
  87. raise "Invalid type #{type}"
  88. end
  89. num_days = options[:time] || 24
  90. interval = options[:interval] || "hour"
  91. mixpanel_options = {
  92. type: type,
  93. unit: interval,
  94. interval: num_days,
  95. limit: 5,
  96. }
  97. if property && value
  98. mixpanel_endpoint = "events/properties/"
  99. mixpanel_options.merge!({
  100. event: event_name,
  101. values: [value],
  102. name: property
  103. })
  104. else
  105. mixpanel_endpoint = "events/"
  106. mixpanel_options.merge!({
  107. event: [event_name]
  108. })
  109. end
  110. data = mixpanel_client.request(mixpanel_endpoint, mixpanel_options)
  111. total_for_events(data)
  112. end
  113. def total_for_events(data)
  114. counts_per_property = data["data"]["values"].collect do |c, values|
  115. values.collect { |k, v| v }.inject(:+)
  116. end
  117. #now, calculate grand total
  118. counts_per_property.inject(:+)
  119. end
  120. ###########################
  121. def number_for_event_using_export(event_name, property, value, num_days = 30)
  122. # TODO:
  123. # MixPanel doesn't understand boolean values for properties
  124. # There is an open ticket, but for now, there is a work around to use export API
  125. # https://mixpanel.com/docs/api-documentation/exporting-raw-data-you-inserted-into-mixpanel
  126. to_date = Date.today
  127. from_date = to_date - num_days
  128. data = mixpanel_client.request('export', {
  129. event: [event_name],
  130. from_date: from_date.to_s,
  131. to_date: to_date.to_s,
  132. where: "boolean(properties[\"#{property}\"]) == #{value} ",
  133. })
  134. data.count
  135. end
  136. end
  137. end