Страницы

вторник, 12 февраля 2013 г.

Google Apps Script. Charts, UrlFetchApp, JSON.

В одной из предыдущих статей я уже затрагивал тему визуализации данных. С тех пор ничего не изменилось, поверьте, да и вряд ли когда-либо изменится: у топ-менеджеров не появилось ни малейшего желания работать с данными, и для принятия решений им все так же необходима информация - то, что они способны понять. Попробуем, стало быть, решить вопрос. Рассмотрим варианты извлечения данных из таблиц и веб-сервисов JSON.

Начнем с таблиц.
Открываем браузер, заходим на drive.google.com. Создадим форму, выберем какую-нибудь красивую тему, назовем ее TestChart.
Сомневаюсь, что нам придется сохранять более 400 тысяч ответов, поэтому для хранения данных будем использовать таблицу.
Открываем форму, заполняем тестовые данные.
Открываем таблицу.

Создадим Standalone скрипт, назовем его TestChartTable.
function doGet() {
  // получаем таблицу
  var ss = SpreadsheetApp.openById('0AkYcK5KeNe1tdGxpQUloZkdUaEprSmhtX1dBTXBJMmc');
  // получаем данные
  var data = ss.getSheets()[0].getDataRange();

  // создаем фильтры
  var nameFilter = Charts.newStringFilter().setFilterColumnIndex(1).build();
  var ageFilter =  Charts.newNumberRangeFilter().setFilterColumnIndex(2).build();
  var browserFilter = Charts.newCategoryFilter().setFilterColumnIndex(3).build(); 

  // создаем диграммы
  var tableChart = Charts.newTableChart()
    .setDataViewDefinition(Charts.newDataViewDefinition().setColumns([1,2,3])).build();
  var pieChart = Charts.newPieChart()
    .setDataViewDefinition(Charts.newDataViewDefinition().setColumns([1,2])).build();

  // создаем панель, привязываем к ней данные, фильтры и диаграммы
  var dashboard = Charts.newDashboardPanel().setDataTable(data)
    .bind([ageFilter, browserFilter, nameFilter], [tableChart, pieChart]).build();

  // рисуем интерфейс
  var app = UiApp.createApplication().setTitle('TestChart').setStyleAttribute('margin', '10px');
  
  var leftPanel = app.createVerticalPanel().add(nameFilter).add(ageFilter).add(browserFilter).add(tableChart); 
  app.add(dashboard.add(app.createHorizontalPanel().add(leftPanel).add(pieChart)));

  return app;
}
Сохраняем версию, публикуем веб-приложение, тестируем.

С данными для "пирога" я слегка не угадал, тем не менее диаграмма от этого не стала менее интерактивной :). Есть возможность сортировки данных, фильтрации по любому показателю, в общем очень похоже на информацию. Попробуйте сами. Для того, чтобы наш "пирог" стал выглядеть более аппетитно, переименуйте заголовок столбца "Возраст" на, к примеру, "Выручка (тысяч рублей)" :).

Переходим к веб-сервису, раздающему данные в формате JSON. В качестве примера я взял первый попавшийся мне в поисковике - GeoNames - географическая база данных имен объектов. Попробуем вытащить из сервиса данные об объектах с заданными координатами (снова первый попавшийся пример).
Создаем скрипт, называем его TestChartJson.
function doGet() {
  var app = UiApp.createApplication().setTitle('TestChart').setStyleAttribute('margin', '10px');
  // получаем HTTPResponse
  var result = UrlFetchApp.fetch('http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&username=demo');
  // парсим JSON
  var json = Utilities.jsonParse(result.getContentText());
  var geonames = json.geonames;
  if (!geonames) { // сервис недоступен
    app.add(app.createLabel('Сервис недоступен'));
    return app;
  }

  Logger.log(geonames);

  return app;
}
Как оказалось, сервис ограничивает количество запросов пользователя demo (последний параметр в URL - username=demo), что, на мой взгляд, не лишено смысла. Но регистрироваться мне не захотелось, поэтому я просто предусмотрел в самом начале кода проверку ответа сервера на наличие данных ожидаемого формата (в районе комментария // сервис недоступен).
Теперь предлагаю вставить в адресную строку браузера URL и посмотреть в каком виде мы должны получить HTTPResponse.
Сохраняем версию скрипта, публикуем веб-приложение, тестируем, в редакторе скрипта читаем данные журнала: Вид - Журналы.

Похоже на правду. Осталось вытащить необходимые данные и красиво их упаковать.
Закомментируем строку "Logger.log(geonames);", после которой добавим код преображения данных.
// собираем таблицу с данными
  var dataTableBuilder = Charts.newDataTable()
    .addColumn(Charts.ColumnType.STRING, 'Наименование')
    .addColumn(Charts.ColumnType.STRING, 'Код')
    .addColumn(Charts.ColumnType.NUMBER, 'Население');

  // закидываем данные
  for (var i in geonames) {
    dataTableBuilder.addRow([geonames[i].toponymName, geonames[i].countrycode, geonames[i].population]);
  }
  var dataTable = dataTableBuilder.build();

  // создаем фильтры
  var nameFilter = Charts.newStringFilter().setFilterColumnLabel('Наименование').build();
  var populationFilter = Charts.newNumberRangeFilter().setFilterColumnLabel('Население').build();

  // создаем диграммы
  var tableChart = Charts.newTableChart()
    .setDataViewDefinition(Charts.newDataViewDefinition().setColumns([0,1,2])).build();
  var pieChart = Charts.newPieChart()
    .setDataViewDefinition(Charts.newDataViewDefinition().setColumns([0,2])).build();

  // создаем панель, привязываем к ней данные, фильтры и диаграммы
  var dashboard = Charts.newDashboardPanel().setDataTable(dataTable)
    .bind([nameFilter, populationFilter], [tableChart, pieChart]).build();

  // интерфейс
  var leftPanel = app.createVerticalPanel().add(nameFilter).add(populationFilter).add(tableChart);
  app.add(dashboard.add(app.createHorizontalPanel().add(leftPanel).add(pieChart)));
Тестируем веб-приложение.

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

В итоге: белые парусиновые штаны в очередной раз обеспечены, креолки и мулатки подтянутся сами :).