Files
habitica/database_reports/count_users_who_own_specified_gear.js

198 lines
5.9 KiB
JavaScript

var authorName = 'Alys'; // in case script author needs to know when their ...
var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
/**
* database_reports/count_users_who_own_specified_gear.js
* https://github.com/HabitRPG/habitica/pull/3884
*/
var thingsOfInterest = {
'Unconventional Armor ownership': {
'data_path': 'items.gear.owned',
'identifyOwnershipWith': 'exists',
'items': [
'eyewear_special_wondercon_red',
'eyewear_special_wondercon_black',
'back_special_wondercon_black',
'back_special_wondercon_red',
'body_special_wondercon_red',
'body_special_wondercon_black',
'body_special_wondercon_gold'
],
},
'Spooky Skins purchases': {
'data_path': 'purchased.skin',
'identifyOwnershipWith': 'true',
'items': [
'monster',
'pumpkin',
'skeleton',
'zombie',
'ghost',
'shadow'
]
}
};
var mongo = require('mongoskin');
var _ = require('lodash');
var dbUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users');
var thingsFound = {}; // each key is one "thing" from thingsOfInterest,
// and the value for that key is the number of users who own it
// (for items, 'owned' values of both true and false are counted
// to include items lost on death)
var query = {}; // Not worth limiting search data with query and fields since
var fields = {}; // this will be run over a local copy of the database?
console.warn('Finding data...');
var progressCount = 1000;
var count = 0;
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
if (err) { return exiting(1, 'ERROR! ' + err); }
if (!user) {
console.warn('All users found.');
return displayData();
}
count++;
_.each(thingsOfInterest,function(obj,label){
var data_path = obj['data_path'];
var items = obj['items'];
var identifyOwnershipWith = obj['identifyOwnershipWith'];
var userOwns = path(user, data_path, {});
_.each(items,function(item){
if ( (identifyOwnershipWith == 'exists' && item in userOwns) ||
(identifyOwnershipWith == 'true' && userOwns[item])
) {
if (! thingsFound[label]) { thingsFound[label] = {}; }
thingsFound[label][item] = (thingsFound[label][item] || 0) + 1;
// console.warn(user.auth.local.username + ": " + label + ": " + item); // good for testing, bad for privacy
}
});
});
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
if (user._id == '9' ) console.warn('lefnire' + ' processed');
});
function displayData() {
var today = yyyymmdd(new Date());
var csvReport = '';
var textReport = today + '\n';
_.each(thingsFound,function(obj,label){
csvReport += '\n"' + label + '"' + '\n';
textReport += '\n' + label + ':\n';
var csvHeader = '"date"'; // heading row in CSV data
var csvData = '"' + today + '"'; // data row in CSV data
var sortedKeys = _.sortBy(_.keys(obj), function(key){ return key; });
_.each(sortedKeys,function(key){
var value = obj[key];
csvHeader += ',"' + key + '"';
csvData += ',"' + (value || 0) + '"';
textReport += '\t' + key + ': ' + value + '\n';
});
csvReport += csvHeader + '\n' + csvData + '\n';
});
console.log('\nCSV DATA:\n' + csvReport + '\n\n' +
'READABLE DATA:\n\n' + textReport + '\n\n');
console.warn(count + ' users searched (should be >400k)\n');
// NB: "should be" assumes that no query filter was applied to findEach
return exiting(0);
}
function path(obj, path, def) {
/**
* Retrieve nested item from object/array
* @param {Object|Array} obj
* @param {String} path dot separated
* @param {*} def default value ( if result undefined )
* @returns {*}
* https://stackoverflow.com/a/16190716
* Usage: console.log(path(someObject, pathname));
*/
for(var i = 0,path = path.split('.'),len = path.length; i < len; i++){
if(!obj || typeof obj !== 'object') return def;
obj = obj[path[i]];
}
if(!obj || typeof obj === 'undefined') return def;
return obj;
}
function yyyymmdd(date) {
var yyyy = date.getFullYear().toString();
var mm = (date.getMonth()+1).toString();
var dd = date.getDate().toString();
return yyyy + "-" + (mm[1]?mm:"0"+mm[0]) + "-" + (dd[1]?dd:"0"+dd[0]);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
/* SAMPLE OUTPUT (STDOUT and STDERR):
$ node database_reports/count_users_who_own_specified_gear.js
Finding data...
Alys processed
lefnire processed
All users found.
CSV DATA:
"Unconventional Armor ownership"
"date","back_special_wondercon_black","back_special_wondercon_red","body_special_wondercon_black","body_special_wondercon_gold","body_special_wondercon_red","eyewear_special_wondercon_black","eyewear_special_wondercon_red"
"2014-09-01","7","7","7","7","7","7","9"
"Spooky Skins purchases"
"date","ghost","monster","pumpkin","shadow","skeleton","zombie"
"2014-09-01","2","3","3","6","4","3"
READABLE DATA:
2014-09-01
Unconventional Armor ownership:
back_special_wondercon_black: 7
back_special_wondercon_red: 7
body_special_wondercon_black: 7
body_special_wondercon_gold: 7
body_special_wondercon_red: 7
eyewear_special_wondercon_black: 7
eyewear_special_wondercon_red: 9
Spooky Skins purchases:
ghost: 2
monster: 3
pumpkin: 3
shadow: 6
skeleton: 4
zombie: 3
400100 users searched (should be >400k)
*/