Статьи

Создайте пользовательскую аналитику с помощью Parse

синтаксический анализ

В настоящее время создание аналитической панели управления критически важно для любого бизнеса. Хотя подписка на Google Analytics является очевидным выбором, иногда нам может потребоваться отслеживать события на более детальном уровне или создавать собственную панель мониторинга. Cassandra отлично подходит для написания аналитического движка, но добавляет дополнительный уровень сложности работы. Это где Parse приходит.

Parse — это IaaS от Facebook. Вы можете использовать его как полнофункциональное хранилище данных, не тратя время на инфраструктуру. В этой статье я собираюсь объяснить, как использовать Parse для создания собственной аналитической панели мониторинга. Читай дальше.

Начиная

В качестве основы мы будем использовать пример приложения, которое я создал для одной из моих предыдущих статей. Вы можете скачать это здесь . Это приложение использует mongoid и HAML, но приведенные здесь примеры также должны работать с Active Record и ERB. С этим из нашего пути, давайте установим основы.

Сначала создайте бесплатную учетную запись с помощью Parse и настройте в ней приложение. Вам понадобится ключ приложения и ключ Javascript, которые вы можете найти на вкладке «Настройки».

Создайте новый файл javascript analytics.js

 // app/assets/javascripts/analytics.js
var CustomAnalytics = {
    init: function (type){
        Parse.initialize(APP_ID, JS_KEY);
    }
}

и включите это в свой макет верхнего уровня:

 # app/views/application.html.haml
!!!
%html
    %head
        %title Build Custom analytics with Parse
        = stylesheet_link_tag        'application', media: 'all', 'data-turbolinks-track' => true
        = javascript_include_tag 'application', 'data-turbolinks-track' => true
        = javascript_include_tag 'vendor/parse', 'data-turbolinks-track' => true
        = javascript_include_tag 'analytics', 'data-turbolinks-track' => true
        = csrf_meta_tags
    %body
        = yield
    :javascript
        // Initialize the Analytics first
        CustomAnalytics.init();

Мы создали пространство имен с именем CustomAnalyticsinit Это предпочтительнее, чем инициализация Parse внутри представления, поскольку при желании вы можете инициализировать несколько поставщиков аналитики, таких как Google или Mixpanel.

Теперь наше приложение готово к общению с серверами Parse.

ПРИМЕЧАНИЕ. Parse использует модель подписки на основе использования. Возможно, вы захотите проверить их тарифные планы до его реализации.

Отслеживание событий

Прежде чем показать, как создать собственный механизм аналитики, давайте сначала взглянем на встроенную библиотеку отслеживания событий Parse, которая похожа на события Google. Это может помочь отследить произвольные события в приложении до удержания пользователя без особой работы с нашей стороны.

В примере приложения есть 2 страницы: одна показывает список категорий, а другая — языки. Допустим, я хочу отслеживать, сколько пользователей нажимают на категории:

 # app/views/category/index.html.haml

    %h1
     Category Listing
    %ul#categories
        - @categories.each do |cat|
            %li
                %a{:href=>"/category/#{cat['id']}", :class=>'js-category-click'}
                    %h3
                        = cat["name"]
                %p
                    = cat["desc"]

и добавьте это в файл макетов:

 # app/views/layouts/application.html.haml
//.......
:javascript
    CustomAnalytics.init();

    $( '.js-category' ).on('click', function(e){
        e.preventDefault();
        var url = e.currentTarget.href;
        Parse.Analytics.track( 'CATEGORY_CLICK', {
            'target': 'category',
        }).then(function(){
                window.location.href = url;
        });
    });
//.............

Здесь мы используем встроенный метод track Требуется 2 параметра: имя события и размеры. Измерения — это пользовательские точки данных, которые мы можем передавать, которые могут быть использованы позже для фильтрации отчетов. Этот метод возвращает обещание. Мы можем выполнить успешный обратный вызов, как только это будет выполнено, в данном случае, перенаправив на исходную ссылку.

Мы должны сделать то же самое для отслеживания событий на языковой странице. Но это много повторяющегося кода. Давайте рефакторинг этого потока кода.

Добавьте это в свой файл analytics.js

 // app/assets/javascripts/analytics.js
var CustomAnalytics = {
    //...
    track: function( name, dimensions, successCallback ){
        Parse.Analytics.track( name, dimensions )
                .then( successCallback );
    }
    //...

}

И измените код отслеживания в вашем файле category.js

 # app/views/layouts/application.html.haml
//.......
:javascript
    //.......

    $( '.js-category' ).on('click', function(e){
        e.preventDefault();
        var url = e.currentTarget.href;
        CustomAnalytics.track( 'CATEGORY_CLICK', {
            'target': 'category',
        }, function(){
                window.location.href = url;
            })
    });
    //.......

Мы передаем те же параметры методу отслеживания. Поначалу это может показаться не очень много, но это уменьшает объем стандартного кода, особенно когда на вашей странице много событий.

parse_events

Чтобы просмотреть отслеживаемые события, перейдите в Analytics -> Events на панели инструментов Parse.

В качестве пользовательского хранилища данных

Мы можем использовать облачные данные Parse для хранения наших пользовательских данных. Он работает очень похоже на хранилище данных NoSQL и довольно гибкий.

Для начала создайте новый класс с именем CategoryClicksData В вашем application.html.haml

 //.........
function trackCloudData( name, id, type ){
  var CategoryClicks = Parse.Object.extend('CategoryClicks'),
      cloud_data = new CategoryClicks();
  //Custom data
  cloud_data.name = name
  cloud_data.type = type
  cloud_data.id = id

  // This syncs the data with the server
  cloud_data.save();
}

//..........

$( '.js-category' ).on('click', function(e){
        e.preventDefault();
        var $elem = $(e.currentTarget),
      url = $elem.url,
      name = $elem.data('name'),
      id = $elem.data('id');

        CustomAnalytics.track( 'CATEGORY_CLICK', {
              'target': 'category',
          }, function(){
                  window.location.href = url;
        });

  trackCloudData(name, id, type);
    });

//.........

Parse.Object Это простая модель Backbone, и мы можем установить для нее пользовательские атрибуты. Когда вы сохраняете эту модель, она синхронизирует данные с сервером.

parse_class

Теперь все данные, которые вы отслеживали, доступны на панели инструментов Parse.

Хранить и получать

Если мы создаем приложение реального времени, мы можем использовать JS API Parse для извлечения данных с сервера. Но для построения панели временных рядов это не сработает. Нам нужно собрать эту информацию из Parse и преобразовать позже в соответствии с нашими потребностями.

Официального Ruby-клиента для Parse не существует, но замечательный gem parse-ruby-client прекрасно подходит.

Добавьте этот драгоценный камень в Gemfile:

 # Gemfile
gem 'parse-ruby-client'

После завершения bundle install

 # app/models/category_analytics.rb

class CategoryAnalytics
  include Mongoid::Document
  include Mongoid::Timestamps

  field :category_id, type: BSON::ObjectId
  field :name, type: String
  field :count, type: Integer

  field :date, type: DateTime
end

Напишите простое задание, которое пройдет по всем категориям и получит запрос на чтение за указанную дату. Поскольку это происходит по сетевому вызову, было бы лучше, если бы мы обрабатывали это асинхронно через resque .

И создайте новую задачу Resque, щелкните по категории aggregator.rb :

 # lib/tasks/category_click_aggregator.rb
class CategoryClickAggregator
    @queue = :category_analytics

    def self.perform()
      Parse.init(:application_id => "APP_ID", :api_key => "API_KEY")
      categories = Category.all
      yesterday = Date.yesterday
      start_date = Parse::Date.new(yesterday.to_time)
      end_date = Parse::Date.new(Date.today.to_time)
      # Convert the dates to Parse date to play nice with their API
      categories.each do |cat|

        count = Parse::Query.new("BookHistory").tap do |q|
          q.eq("category_id", cat.id)
          q.greater_eq("createdAt", start_date)
          q.less_eq("createdAt", end_date)
        end.get.count

        # See if this exists already
        category_analytics = CategoryAnalytics.find_by(:category_id => cat.id, :date => yesterday )

        if category_analytics.nil?
          category_analytics = CategoryAnalytics.new
          category_analytics.name = cat.name
          category_analytics.category_id = cat.id
          category_analytics.date = yesterday
        end
        category_analytics.count = count
        category_analytics.save
      end
    end
end

Модуль Parse::QueryPOST Затем мы получаем результаты, агрегируем их и сохраняем в локальной базе данных для поддержки генерации отчетов временных рядов.

Это просто простая демонстрация того, как извлечь данные из Parse. Однако в производстве я бы рекомендовал запускать отдельное задание, которое проходит по всем категориям и ставит задания в очередь индивидуально. Таким образом, задачи могут быть возобновлены, когда они терпят неудачу вместо всего банка. Кроме того, по мере роста данных мы можем порождать нескольких рабочих и выполнять их параллельно.

Ограничения

Все запросы к Parse разбиты на страницы, и по умолчанию ограничение на количество страниц составляет 100. Если у вас более 100 записей в наборе результатов, приведенный выше код вернет неверные результаты. Мы можем увеличить лимит вручную до 1000, но это все равно постигнет та же участь, что и ваши события.

Чтобы исправить это должным образом, нам придется прибегнуть к уродливому циклу do..while

 total_count = 0
offset = 0
loop do
  count = Parse::Query.new("BookHistory").tap do |q|
    q.eq("category_id", cat.id)
    q.greater_eq("createdAt", start_date)
    q.less_eq("createdAt", end_date)
    q.limit(1000)
    q.offset(offset)
  end.get.count
  total_count+= count
  offset++
  break if count < 1000
end

Завершение

Parse — отличный инструмент, и мы едва поцарапали его поверхность. Например, мы можем использовать его для аутентификации и отслеживания пользователей, использования заданий для запуска пользовательских заданий в облаке и настройки веб-хуков. Надеюсь, я заинтересовался этой статьей. Код, используемый в этой статье, доступен здесь . Не стесняйтесь присоединиться к обсуждению в комментариях.