Skip to content

Commit f10cbe6

Browse files
committed
1 parent 5e2811d commit f10cbe6

File tree

3 files changed

+197
-1
lines changed

3 files changed

+197
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
.access-token

metadata.py

+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# by Erik Osheim
5+
#
6+
# Reads README.md, and writes a README.md.new. If the format of
7+
# README.md changes, this script may need modifications.
8+
#
9+
# Currently it rewrites each section, doing the following:
10+
# 1. alphabetizing
11+
# 2. querying GitHub for stars and days since active
12+
# 3. formatting the link title to show this info
13+
# 4. bolding projects with lots of stars
14+
#
15+
# Once README.md has the stars/days info in the links, the
16+
# repo_regex will need slight modification.
17+
#
18+
# In order to use GH authentication, create a file in this directory
19+
# called .access-token, whose contents are: "$user:$token" where $user
20+
# is your github username, and $token is a Personal Access Token.
21+
22+
import base64
23+
import datetime
24+
import json
25+
import os.path
26+
import random
27+
import re
28+
import shutil
29+
import sys
30+
import urllib2
31+
32+
# we use these regexes when "parsing" README.md
33+
empty_regex = re.compile(r"^ *\n$")
34+
section_regex = re.compile(r"^## (.+)\n$")
35+
repo_regex = re.compile(r"^\* (?:\*\*)?\[?([^*★]+[^ ★])(?: ★ ([^ ]+) ⧗ ([^ *]+))?\]\((.+?)\)(?:\*\*)?(?: (?:-|—|–) (.+))?\n$")
36+
end_regex = re.compile(r"^# .+\n$")
37+
github_regex = re.compile(r"^https://github.com/(.+?)/(.+?)(?:/?)$")
38+
39+
# some paths
40+
readme_path = 'README.md'
41+
temp_path = 'README.md.new'
42+
43+
# these will be updated if .access-token exists.
44+
user = None
45+
token = None
46+
47+
# use fake to avoid hitting github API
48+
fake = True
49+
50+
# whether to query all projects, or just those lacking scores/days.
51+
full_update = False
52+
53+
# right now.
54+
now = datetime.datetime.now()
55+
56+
# ask github for the number of stargazers, and days since last
57+
# activity, for the given github project.
58+
def query(owner, name):
59+
if fake:
60+
print ' {0}/{1}: ok'.format(owner, name)
61+
return (random.randint(1, 1000), random.randint(1, 300))
62+
else:
63+
try:
64+
req = urllib2.Request('https://api.github.com/repos/{0}/{1}'.format(owner, name))
65+
if user is not None and token is not None:
66+
b64 = base64.encodestring('{0}:{1}'.format(user, token)).replace('\n', '')
67+
req.add_header("Authorization", "Basic {0}".format(b64))
68+
u = urllib2.urlopen(req)
69+
j = json.load(u)
70+
t = datetime.datetime.strptime(j['updated_at'], "%Y-%m-%dT%H:%M:%SZ")
71+
days = max(int((now - t).days), 0)
72+
print ' {0}/{1}: ok'.format(owner, name)
73+
return (int(j['stargazers_count']), days)
74+
except urllib2.HTTPError, e:
75+
print ' {0}/{1}: FAILED'.format(owner, name)
76+
return (None, None)
77+
78+
def output_repo(outf, name, stars, days, link, rdesc):
79+
popular = stars is not None and int(stars) >= 500
80+
if stars is None and days is None:
81+
title = name
82+
else:
83+
title = '%s ★ %s ⧗ %s' % (name, stars, days)
84+
if popular:
85+
outf.write('* **[{0}]({1})** - {2}\n'.format(title, link, rdesc))
86+
else:
87+
outf.write('* [{0}]({1}) - {2}\n'.format(title, link, rdesc))
88+
89+
def flush_section(outf, section, sdesc, repos):
90+
print ' ' + section.strip()
91+
outf.write(section)
92+
outf.write('\n')
93+
if sdesc:
94+
outf.write(sdesc)
95+
outf.write('\n')
96+
repos.sort(key=lambda t: t[0].lower())
97+
for name, stars, days, link, rdesc in repos:
98+
if not full_update and stars is not None and days is not None:
99+
output_repo(outf, name, stars, days, link, rdesc)
100+
continue
101+
102+
m = github_regex.match(link)
103+
if not m:
104+
print ' {0}: not a repo'.format(link)
105+
output_repo(outf, name, stars, days, link, rdesc)
106+
continue
107+
108+
stars, days = query(m.group(1), m.group(2))
109+
output_repo(outf, name, stars, days, link, rdesc)
110+
outf.write('\n')
111+
112+
def run():
113+
if full_update:
114+
print 'querying for all entries'
115+
else:
116+
print 'querying for new entries only'
117+
118+
if fake:
119+
print 'running in fake mode -- no GH queries will be made'
120+
121+
if os.path.exists('.access-token'):
122+
global user, token
123+
user, token = open('.access-token').read().strip().split(':')
124+
print 'using Personal Access Token {0}:{1}'.format(user, token)
125+
else:
126+
print 'no Personal Access Token found in .access-token'
127+
128+
inf = open(readme_path, 'r')
129+
lines = list(inf)
130+
inf.close()
131+
print 'read {0}'.format(readme_path)
132+
133+
started = False
134+
finished = False
135+
section = None
136+
sdesc = None
137+
repos = []
138+
outf = open(temp_path, 'w')
139+
140+
total_repos = 0
141+
142+
print 'writing {0}'.format(temp_path)
143+
for line in lines:
144+
if finished:
145+
outf.write(line)
146+
elif started:
147+
if end_regex.match(line):
148+
total_repos += len(repos)
149+
flush_section(outf, section, sdesc, repos)
150+
outf.write(line)
151+
finished = True
152+
elif empty_regex.match(line):
153+
continue
154+
elif section_regex.match(line):
155+
total_repos += len(repos)
156+
flush_section(outf, section, sdesc, repos)
157+
section = line
158+
sdesc = None
159+
repos = []
160+
else:
161+
m = repo_regex.match(line)
162+
if m:
163+
name, stars, days, link, rdesc = m.groups()
164+
repos.append((name, stars, days, link, rdesc))
165+
elif sdesc is None:
166+
sdesc = line
167+
else:
168+
raise Exception("cannot parse {0}".format(line))
169+
else:
170+
if section_regex.match(line):
171+
section = line
172+
started = True
173+
else:
174+
outf.write(line)
175+
outf.close()
176+
print 'wrote {0} repos to {1}'.format(total_repos, temp_path)
177+
178+
print 'moving {0} to {1}'.format(temp_path, readme_path)
179+
shutil.move(temp_path, readme_path)
180+
181+
if __name__ == "__main__":
182+
#global fake, full_update
183+
184+
from optparse import OptionParser
185+
186+
parser = OptionParser()
187+
parser.add_option("-f", "--fake", action="store_true", dest="fake",
188+
default=False, help="don't query github, use fake data")
189+
parser.add_option("-u", "--update", action="store_true", dest="update",
190+
default=False, help="update all entries to newest data")
191+
192+
opts, _ = parser.parse_args()
193+
fake = opts.fake
194+
full_update = opts.update
195+
run()

node.js

-1
This file was deleted.

0 commit comments

Comments
 (0)