Продолжая играть с IBM Bluemix , на этой неделе я провел некоторое время, играя с сервисом Personality Insights . Этот сервис использует IBM Watson для анализа текстового ввода и определения личностных аспектов автора. Он фокусируется на трех областях анализа:
- Определение потребностей автора. Эти потребности сужаются до двенадцати основных областей и оцениваются по шкале от 0 до 100 процентилей. Потребности: волнение, гармония, любопытство, идеал, близость, самовыражение, свобода, любовь, практичность, стабильность, вызов и структура.
- Определение того, что ценности автора. Ватсон считает, что это важно для автора. Как и в случае с потребностями, ценности сосредоточены на наборе основных элементов: самопреодоление / помощь другим, сохранение / традиция, гедонизм / получение удовольствия от жизни, самосовершенствование / достижение успеха и открытость к изменениям / волнению.
- Наконец, служба PI сообщает о «Большой пятерке» — это модель личности, которая пытается описать, как человек взаимодействует с миром.
Как вы можете себе представить, это довольно глубокие вещи, и, честно говоря, моя жена, которая работает над ее степенью социологии, вероятно, лучше понимала бы результаты. Вы можете просмотреть полную документацию, а также ознакомиться с документацией по API для получения дополнительной информации.
Для моей демонстрации я решил попробовать что-нибудь интересное. Сервис PI работает лучше всего, когда в нем не менее 3500 слов. Типичное сообщение в блоге может содержать около пятисот слов, и, поскольку в типичном RSS-канале содержится десять элементов, я решил создать приложение, которое будет анализировать RSS-канал и пытаться определить личность автора. Я назвал это Сканированием Личности Блога . Я дам ссылку на демо через минуту, но давайте сначала посмотрим на код.
Сначала мы рассмотрим файл app.js для приложения Node. Это довольно тривиально, поскольку есть только два просмотра — домашняя страница и API для отправки URL-адреса RSS.
/*jshint node:false */ /* global console,require */ var express = require('express'); var hbs = require('hbs'); var url = require('url'); hbs.registerHelper('raw-helper', function(options) { return options.fn(); }); var rssReader = require('./rssreader.js'); var insightsAPI = require('./insights.js'); // setup middleware var app = express(); app.use(app.router); app.use(express.errorHandler()); app.use(express.static(__dirname + '/public')); //setup static public directory app.set('view engine', 'html'); app.engine('html', hbs.__express); app.set('views', __dirname + '/views'); //optional since express defaults to CWD/views // render index page app.get('/', function(req, res){ res.render('index'); }); app.get('/parse', function(req, res) { var url_parts = url.parse(req.url, true); var query = url_parts.query; if(!query.rss) { res.json({error:"Invalid data sent."}); return; } rssReader.parse(query.rss, function(err,content) { if(err) { res.json(err); } else { console.log('bak with content, len is '+content.length); insightsAPI.parse(query.rss, query.rss, content, function(data) { console.log('back from IAPI'); //console.log(JSON.stringify(data)); res.json(data); }); } }); }); // There are many useful environment variables available in process.env. // VCAP_APPLICATION contains useful information about a deployed application. var appInfo = JSON.parse(process.env.VCAP_APPLICATION || "{}"); // TODO: Get application information and use it in your app. // VCAP_SERVICES contains all the credentials of services bound to // this application. For details of its content, please refer to // the document or sample of each service. if(process.env.VCAP_SERVICES) { var services = JSON.parse(process.env.VCAP_SERVICES || "{}"); console.log(services); var apiUrl = services.personality_insights[0].credentials.url; var apiUsername = services.personality_insights[0].credentials.username; var apiPassword = services.personality_insights[0].credentials.password; } else { var credentials = require('./credentials.json'); var apiUrl = credentials.apiUrl; var apiUsername = credentials.apiUsername; var apiPassword = credentials.apiPassword; } insightsAPI.setAuth(apiUrl, apiUsername, apiPassword); // The IP address of the Cloud Foundry DEA (Droplet Execution Agent) that hosts this application: var host = (process.env.VCAP_APP_HOST || 'localhost'); // The port on the DEA for communication with the application: var port = (process.env.VCAP_APP_PORT || 3000); // Start server app.listen(port, host); console.log('App started on port ' + port);
Не очень захватывающе, и я не поделился бы этим, как обычно, но я специально хотел назвать те моменты, на которые смотрят process.env.VCAP_SERVICES
. Вот как мое приложение собирает учетные данные API при работе в среде Bluemix.
За чтением RSS отвечает пакет NPM feedparser . Это тот же, который я использовал для ColdFusionBloggers.org . Я пропущу этот код, так как он не такой захватывающий.
Самое интересное происходит в коде, который используется для взаимодействия со службой PI:
var https = require('https'); var querystring = require('querystring'); var url = require('url'); var apiUsername; var apiPassword; var apiUrl; var apiHost; var apiPath; function setAuth(apiurl, u, p) { apiUrl = apiurl; apiUsername=u; apiPassword=p; var parts = url.parse(apiUrl); apiHost = parts.host; apiPath = parts.pathname; } function sendInsights(user,source,input,cb) { //cb(fake);return; var data = {"contentItems":[]}; var item = {}; item.userid = user; item.sourceid = source; this.id = this.userid + '_'+this.sourceid; item.contenttype = "text/plain"; //todo - remove html from input. the service does it, but we can do it ourselves item.language = "en"; item.content = input; data.contentItems.push(item); var postData = JSON.stringify(data); var options = { host: apiHost, port: 443, path: apiPath + "/v2/profile", headers: { 'Authorization': 'Basic ' + new Buffer(apiUsername + ':' + apiPassword).toString('base64'), 'Content-Type':'application/json', 'Content-Length': Buffer.byteLength(postData) }, method:"post" }; console.log(options); var req = https.request(options, function(resp) { var body = ""; resp.on("data", function(chunk) { body += chunk; }); resp.on("end", function() { //console.log("done");console.log(body); cb(JSON.parse(body)); }); }); req.write(postData); req.end(); }; var InsightAPI = { setAuth:setAuth, parse:sendInsights }; module.exports = InsightAPI;
Хорошо, возможно, «захватывающий» немного. Честно говоря, это всего лишь HTTP-хит и JSON-ответ. Просто — но в этом-то и дело. Хороший сервис должен быть довольно простым в использовании.
Остальные просто представляли результаты. Ребята из Bluemix создали классное демо с графиками и прочим , но я решил сделать его простым и просто отобразить значения — отсортировано. Я использовал Handlebars, чтобы сделать его немного приятнее, и это меня немного смутило. Мне никогда не приходило в голову подумать, что произойдет, когда я использую шаблон Handlebars для клиентской части в представлении, которое запускается приложением Node.js, использующим Handlebars также на клиенте. Как вы можете догадаться, сначала это не сработало. Если вы посмотрите на этот первый список кода, вы увидите помощника с именем raw-helper. Мне нужно было добавить это, чтобы я мог использовать синтаксис Handlebar в моем представлении и чтобы сервер игнорировал его. Вот как это выглядит в index.html:
<script id="reportTemplate" type="text/x-handlebars-template"> {{{{raw-helper}}}} <div class="row"> <div class="col-md-4"> <h2>Values</h2> {{#each values}} <div class="row"> <div class="col-md-6"><strong>{{name}}</strong></div> <div class="col-md-6">{{perc percentage}}</div> </div> {{/each}} </div> <div class="col-md-4"> <h2>Needs</h2> {{#each needs}} <div class="row"> <div class="col-md-6"><strong>{{name}}</strong></div> <div class="col-md-6">{{perc percentage}}</div> </div> {{/each}} </div> <div class="col-md-4"> <h2>The Big 5</h2> {{#each big5}} <div class="row"> <div class="col-md-6"><strong>{{name}}</strong></div> <div class="col-md-6">{{perc percentage}}</div> </div> {{#each children}} <div class="row"> <div class="col-md-offset12 col-md-5 text-muted">{{name}}</div> <div class="col-md-6 text-muted">{{perc percentage}}</div> </div> {{/each}} {{/each}} </div> </div> {{{{/raw-helper}}}} </script>
После того, как я заработал это, я в основном был в порядке, но потом я сделал глупое дерьмо, как добавление помощника в Node.js app.js, когда он мне действительно был нужен в клиентском app.js. Я, наверное, не должен был называть эти файлы одинаковыми. Так как же выглядят результаты? Я собираюсь дать ссылку на демо, конечно, но вот несколько примеров. Во-первых, мой собственный блог:
Далее Грубер отважного огненного шара :
И, наконец, блог Сары Пэйлин :
Хотите попробовать сами? Проверьте демо здесь: http://bloginsights.mybluemix.net/ . Вы можете увидеть весь исходный код проекта здесь: https://github.com/cfjedimaster/bloginsights .