├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── check-logins.coffee ├── format-languages.coffee ├── format-users.coffee ├── get-details.coffee ├── get-users.coffee ├── package.json └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *~ 7 | 8 | # OS or Editor folders 9 | .DS_Store 10 | .cache 11 | Icon? 12 | 13 | # Folders to ignore 14 | .hg 15 | .svn 16 | 17 | # Node.js package manager 18 | /node_modules 19 | /npm-debug.log 20 | 21 | # Other stuff 22 | *.pyc 23 | /tmp 24 | 25 | # Project stuff 26 | /temp-logins.json 27 | /old-logins.json 28 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "formatted"] 2 | path = formatted 3 | url = git@github.com:2657075.git 4 | [submodule "raw"] 5 | path = raw 6 | url = git@github.com:4524946.git 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: get format 2 | get: 1 2 3 | format: 3 4 | 5 | 1: 6 | if [ -e temp-logins.json ]; then mv temp-logins.json old-logins.json; fi; 7 | npx coffee get-users.coffee 8 | # for debug - requires get-users.coffee/get-details.coffee already ran: 9 | #npx coffee check-logins.coffee 10 | 11 | 2: 12 | npx coffee get-details.coffee 13 | 14 | 3: 15 | npx coffee format-languages.coffee 16 | npx coffee format-users.coffee 17 | 18 | 4: sync-raw sync-formatted 19 | 20 | sync: sync-raw sync-formatted 21 | force-sync: force-sync-raw sync-formatted 22 | 23 | sync-raw: 24 | cd raw && git commit -am 'Update stats.' && git push 25 | 26 | force-sync-raw: 27 | cd raw && git commit -am 'Update stats.' --amend && git push --force 28 | 29 | sync-formatted: 30 | cd formatted && git commit -am 'Sync.' --amend && git push --force 31 | 32 | clean: 33 | rm temp-logins.json 34 | rm old-logins.json 35 | rm raw/github-languages-stats.json 36 | rm raw/github-users-stats.json 37 | rm formatted/active.md 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub top users 2 | 3 | Generated stats: [git.io/top](http://git.io/top). 4 | 5 | ## Usage 6 | 7 | Make sure you’ve got node.js and coffeescript installed. 8 | 9 | ```bash 10 | # Install deps. 11 | npm install 12 | # Download and format everything. 13 | make 14 | 15 | # or 16 | 17 | # Download data. 18 | make get 19 | 20 | # Generate stats. 21 | make format 22 | ``` 23 | 24 | 25 | ## License 26 | 27 | The MIT License (MIT) 28 | 29 | Copyright (c) 2013 Paul Miller (http://paulmillr.com/) 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the “Software”), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | -------------------------------------------------------------------------------- /check-logins.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | utils = require './utils' 3 | fs = require 'fs' 4 | data = require './raw/github-users-stats.json' 5 | prev = require './old-logins.json' 6 | curr = require './temp-logins.json' 7 | 8 | filtered = prev 9 | .filter(utils.isNotIn(curr)) 10 | .map(utils.reverseFind(data)) 11 | .filter((_) -> _) 12 | .map (_) -> 13 | login: _.login, followers: _.followers 14 | .sort (a, b) -> 15 | b.followers - a.followers 16 | 17 | console.log 'Filtered logins:' 18 | console.log filtered 19 | console.log JSON.stringify filtered.map(utils.prop 'login'), null, 2 20 | -------------------------------------------------------------------------------- /format-languages.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | utils = require './utils' 3 | 4 | getLanguageStats = (inputFile, outFile) -> 5 | stats = require inputFile 6 | total = stats.length 7 | unsorted = Total: total 8 | stats.forEach (stat) -> 9 | {language} = stat 10 | return unless language 11 | unsorted[language] ?= 0 12 | unsorted[language] += 1 13 | 14 | languages = {} 15 | Object.keys(unsorted) 16 | .sort (a, b) -> 17 | unsorted[b] - unsorted[a] 18 | .forEach (language) -> 19 | languages[language] = unsorted[language] 20 | 21 | utils.writeStats outFile, languages 22 | 23 | getLanguageStats './raw/github-users-stats.json', './raw/github-languages-stats.json' 24 | -------------------------------------------------------------------------------- /format-users.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | fs = require 'fs' 3 | 4 | # Reducer. 5 | minimum = (min, current) -> 6 | if current < min 7 | current 8 | else 9 | min 10 | 11 | top = (stats, field, type) -> 12 | get = (stat) -> 13 | value = stat[field] 14 | if type is 'list' then value.length else value 15 | 16 | format = (stat) -> 17 | value = get stat 18 | switch type 19 | when 'thousands' then "#{(value / 1000)}k" 20 | else value 21 | 22 | stats 23 | .slice() 24 | .sort (a, b) -> 25 | get(b) - get(a) 26 | .slice(0, 15) 27 | .map (stat) -> 28 | login = stat.login 29 | "[#{login}](https://github.com/#{login}) (#{format stat})" 30 | .join ', ' 31 | 32 | stats2markdown = (datafile, mdfile, title) -> 33 | stats = require(datafile) 34 | minFollowers = stats.map((_) -> _.followers).reduce(minimum, 1000) 35 | maxNumber = 256 36 | 37 | today = new Date() 38 | from = new Date() 39 | from.setYear today.getFullYear() - 1 40 | 41 | out = """ 42 | # Most active GitHub users ([git.io/top](http://git.io/top)) 43 | 44 | The count of contributions (summary of Pull Requests, opened issues and commits) to public repos at GitHub.com from **#{from.toGMTString()}** till **#{today.toGMTString()}**. 45 | 46 | Only first 1000 GitHub users according to the count of followers are taken. 47 | This is because of limitations of GitHub search. Sorting algo in pseudocode: 48 | 49 | ```javascript 50 | githubUsers 51 | .filter(user => user.followers > #{minFollowers}) 52 | .sortBy('contributions') 53 | .slice(0, #{maxNumber}) 54 | ``` 55 | 56 | Made with data mining of GitHub.com ([raw data](https://gist.github.com/4524946), [script](https://github.com/paulmillr/top-github-users)) by [@paulmillr](https://github.com/paulmillr) with contribs of [@lifesinger](https://github.com/lifesinger) and [@ahmetalpbalkan](https://github.com/ahmetalpbalkan). Updated once per week. 57 | 58 |
| # | 60 |User | 61 |Contribs | 62 | 63 |Location | 64 |Picture | 65 | \n 66 | """ 67 | 68 | rows = stats 69 | .filter((stat) -> stat.contributions < 20000).slice(0, maxNumber).map (stat, index) -> 70 | """ 71 |
|---|---|---|---|---|
| ##{index + 1} | 73 |#{stat.login}#{if stat.name then ' (' + stat.name + ')' else ''} | 74 |#{stat.contributions} | 75 | 76 |#{stat.location} | 77 |