Skip to content

Commit

Permalink
added tests, this thing is ready for prime time
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Coe committed Jan 2, 2016
1 parent 1a0da9a commit 8bfad3d
Show file tree
Hide file tree
Showing 8 changed files with 742 additions and 42 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
# top-npm-users
# top-npm-users ([git.io/npm-top](http://git.io/npm-top))

[![Build Status](https://travis-ci.org/bcoe/top-npm-users.png)](https://travis-ci.org/bcoe/top-npm-users)
[![Coverage Status](https://coveralls.io/repos/bcoe/top-npm-users/badge.svg?branch=master)](https://coveralls.io/r/bcoe/top-npm-users?branch=master)
[![NPM version](https://img.shields.io/npm/v/top-npm-users.svg)](https://www.npmjs.com/package/top-npm-users)

npm users sorted by the monthly downloads of their modules.

Inspired by [top-github-users](https://github.com/paulmillr/top-github-users)

## Installing

```
npm i top-npm-users -g
```

## Usage

Generate the statistics:

```sh
top-npm-users calculate
```

Generate the Markdown:

```sh
top-npm-users render
```

## How Counts Are Calculated

top-npm-users walks a stream of the npm registry using [changes-stream](https://www.npmjs.com/package/changes-stream) and pulls down statistics
from the [npm download counts api](https://github.com/npm/download-counts).

## License

ISC
15 changes: 8 additions & 7 deletions bin/top-npm-users.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#!/usr/bin/env node

var DownloadCounts = require('../')
var TopUsers = require('../')

require('yargs')
.command('update-counts', 'walk the registry changes feed and update user download counts', function (yargs, argv) {
var dc = new DownloadCounts()
dc.updateCounts()
.usage('$0 [command]')
.command('calculate', 'walk the registry changes feed and calculate user download counts', function (yargs, argv) {
var dc = new TopUsers()
dc.calculate()
})
.command('render-top-users', 'read in npm-top-users.json and output the markdown report', function (yargs, argv) {
var dc = new DownloadCounts()
dc.renderTopUsers()
.command('render', 'read in npm-top-users.json and render the markdown report', function (yargs, argv) {
var dc = new TopUsers()
dc.render()
})
.help('h')
.alias('h', 'help')
Expand Down
51 changes: 25 additions & 26 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,44 @@
var _ = require('lodash')
var ChangesStream = require('changes-stream')
var fs = require('fs')
var handlebars = require('handlebars')
var mkdirp = require('mkdirp')
var moment = require('moment')
var queue = require('async').queue
var request = require('request')

function DownloadCounts (opts) {
function TopUsers (opts) {
_.extend(this, {
saveInterval: 15000,
dirty: false,
countsUrl: 'https://api.npmjs.org/downloads/point/last-month/',
rawData: './output/maintainers.json',
outputDirectory: './output/',
jsonData: 'npm-top-users.json',
templateName: 'npm-top-users.md',
registryDb: 'https://skimdb.npmjs.com/registry',
downloadCounts: {}
downloadCounts: {},
ChangesStream: require('changes-stream')
}, opts)
}

DownloadCounts.prototype.updateCounts = function () {
TopUsers.prototype.calculate = function () {
var _this = this

this._q = queue(function (task, callback) {
request.get({
json: true,
url: _this.countsUrl + encodeURIComponent(task.name)
}, function (err, res, obj) {
if (err) {
task.retries = task.retries || 0
if (task.retries < 3) {
task.retries++
_this._q.push(task)
}
return callback(err)
}
if (err) return callback(err)
if (res.statusCode !== 200) return callback(Error('unexpected status = ' + res.statusCode))
_this.dirty = true
_this._updateUserCounts(task, obj.downloads)
return callback()
})
}, 5)

var changes = new ChangesStream({
var changes = new this.ChangesStream({
include_docs: true,
db: 'https://skimdb.npmjs.com/registry'
db: this.registryDb
})

changes.on('readable', function () {
Expand All @@ -61,25 +56,25 @@ DownloadCounts.prototype.updateCounts = function () {
}
})

setInterval(function () {
this._saveInterval = setInterval(function () {
if (_this.dirty) {
console.log('saving download counts (q = ' + _this._q.length() + ')')
mkdirp.sync('./output')
fs.writeFileSync('./output/maintainers.json', JSON.stringify(_this.downloadCounts, null, 2), 'utf-8')
mkdirp.sync(_this.outputDirectory)
fs.writeFileSync(_this.outputDirectory + _this.jsonData, JSON.stringify(_this.downloadCounts, null, 2), 'utf-8')
}
}, this.saveInterval)
}

DownloadCounts.prototype._updateUserCounts = function (task, downloads) {
TopUsers.prototype._updateUserCounts = function (task, downloads) {
var _this = this
task.maintainers.forEach(function (maintainer) {
if (!_this.downloadCounts[maintainer.name]) _this.downloadCounts[maintainer.name] = 0
_this.downloadCounts[maintainer.name] += downloads || 0
})
}

DownloadCounts.prototype.renderTopUsers = function () {
var template = handlebars.compile(fs.readFileSync('./npm-top-users.md.mustache', 'utf-8'))
TopUsers.prototype.render = function () {
var template = handlebars.compile(fs.readFileSync(this.templateName + '.mustache', 'utf-8'))

var result = template({
end: moment().format('ll'),
Expand All @@ -93,12 +88,12 @@ DownloadCounts.prototype.renderTopUsers = function () {
})
})

mkdirp.sync('./output')
fs.writeFileSync('./output/npm-top-users.md', result, 'utf-8')
mkdirp.sync(this.outputDirectory)
fs.writeFileSync(this.outputDirectory + this.templateName, result, 'utf-8')
}

DownloadCounts.prototype._top100Users = function () {
var userMap = JSON.parse(fs.readFileSync(this.rawData, 'utf-8'))
TopUsers.prototype._top100Users = function () {
var userMap = JSON.parse(fs.readFileSync(this.outputDirectory + this.jsonData, 'utf-8'))
var users = []

Object.keys(userMap).forEach(function (k) {
Expand All @@ -115,4 +110,8 @@ DownloadCounts.prototype._top100Users = function () {
return users.slice(0, 100)
}

module.exports = DownloadCounts
TopUsers.prototype.stop = function () {
if (this._saveInterval) clearInterval(this._saveInterval)
}

module.exports = TopUsers
4 changes: 2 additions & 2 deletions npm-top-users.md.mustache
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# npm Users By Downloads ([git.io/npm-top](git.io/npm-top))
# npm Users By Downloads ([git.io/npm-top](http://git.io/npm-top))

----------

npm users sorted by the monthly downloads of their modules, for the range _{{start}}_ until _{{end}}_.

Metrics are calculated using _https://api.npmjs.org_, and _https://skimdb.npmjs.com_.
Metrics are calculated using [top-npm-users](https://github.com/bcoe/top-npm-users).

| **#** | User | Downloads |
| ----- | ---- | --------- |
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"devDependencies": {
"chai": "^3.4.1",
"nock": "^3.6.0",
"rimraf": "^2.5.0",
"standard": "^5.4.1",
"tap": "^4.0.0"
},
Expand Down
Loading

0 comments on commit 8bfad3d

Please sign in to comment.