-
Notifications
You must be signed in to change notification settings - Fork 63
/
update.py
220 lines (172 loc) · 6.16 KB
/
update.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import json
import requests
import datetime
import sys
import os
import locale
import dotenv
def readme_header(file):
"""
Write the header of the `README.md` being generated.
This includes an explanation of what the repository is for, and the
criteria for language inclusion, as well as the header of the Markdown
table being generated.
"""
header = (
"# Languages Written in Rust\n"
"\n"
"This is a (probably incomplete) list of languages implemented in\n"
"Rust. It is intended as a source of inspiration and comparison, and as a\n"
"directory of potentially interesting projects in this vein.\n"
"\n"
"## What Can Be Included?\n"
"\n"
"1. Is it a language?\n"
"2. Is it written in Rust?\n"
"\n"
"Then it can be included in this list!\n"
"\n"
"## List of Languages\n"
"\n"
"| Name | ⭐ Stars | ☀️ Status | Description |\n"
"|:-----|:---------|:-----------|:-----------|\n"
)
file.write(header)
def readme_footer(file):
"""
Write the footer of the `README.md` being generated.
This includes any additional information which is to be written _after_ the
table of languages.
"""
footer = (
"\n"
"*: Parcel is a large project of which the JavaScript transformer (written in Rust)\n"
'is a small part. The "stars" number here reflects the whole project, which is\n'
"broader than a programming language project.\n"
"\n"
)
file.write(footer)
def key(e):
"""
Gets the star count for an entry in the language list.
This is used to sort languages when the table is generated.
"""
return e["stars"]
def extract_languages():
"""
Load languages from the `languages.json` file, returning
`active_langs`, `inactive_langs`, and `lang_names` to be
used when generating the language table.
"""
with open("languages.json", "r", encoding="utf-8") as file:
data = json.load(file)
active_langs = []
inactive_langs = []
lang_names = []
for lang in data:
lang_names.append({"name": lang["name"], "url": lang["url"]})
if lang["active"]:
active_langs.append(lang)
else:
inactive_langs.append(lang)
active_langs.sort(reverse=True, key=key)
inactive_langs.sort(reverse=True, key=key)
return active_langs, inactive_langs, lang_names
def api_request(token):
"""
Update `languages.json` based on the latest API data for each language.
"""
with open("languages.json", "r+", encoding="utf-8") as file:
# Read the current state of the file.
languages = json.load(file)
new_data = []
for lang in languages:
name = lang["name"]
# TODO: Also support requests to other APIs like GitLab to get this info.
if "github.com" in lang["url"]:
print(f"Updating...\t{name:<30}\r", end="")
# Get the URL for the proper API request.
url = lang["url"].replace("github.com", "api.github.com/repos")
# Make the API request with the passed-in token.
response = requests.get(
url, headers={f"Authorization": f"token {token}"}
)
# Print an error if something fails, but keep going.
if response.status_code != 200:
print(f"API request failed for {url}", file=sys.stderr)
continue
# Get the latest data and update the list.
data = response.json()
new_data.append(
{
"name": lang["name"],
"url": lang["url"],
"description": data["description"],
"stars": data["stargazers_count"],
"active": is_active(data["pushed_at"]),
}
)
else:
print(f"Skipping... {name:<30}\r", end="")
# Update the file.
file.seek(0)
json.dump(new_data, file, indent=4)
file.truncate()
# Print a newline at the end.
nothing = ""
print(f"Done! {nothing:>30}")
def is_active(raw_updated_at):
"""
Determine whether a project has been updated recently enough to treat as
"active" in the table.
"""
updated_at = datetime.datetime.strptime(raw_updated_at, "%Y-%m-%dT%H:%M:%SZ")
delta = datetime.timedelta(weeks=24)
date = updated_at + delta
now = datetime.datetime.now()
return date >= now
def write_readme():
"""
Write the current contents of `languages.json` out to the `README.md` file.
"""
# Get the current data from the JSON file.
active, inactive, languages = extract_languages()
with open("README.md", "w", encoding="utf-8") as out:
out.seek(0)
# Write the header.
readme_header(out)
# Write the active language entries.
for i in active:
out.write(
f"| [{i['name']}] | {i['stars']:,d} | ☀️ Active | {i['description']} |\n"
)
# Write the inactive language entries.
for i in inactive:
out.write(
f"| [{i['name']}] | {i['stars']:,d} | 🌙 Inactive | {i['description']} |\n"
)
# Write the footer.
readme_footer(out)
# Write the Markdown link entries.
for i in languages:
out.write(f"[{i['name']}]: {i['url']}\n")
out.truncate()
if __name__ == "__main__":
dotenv.load_dotenv()
try:
if len(sys.argv) != 2:
print("usage: python update.py [api] [readme]")
exit(1)
elif sys.argv[1] == "readme":
write_readme()
exit(0)
elif sys.argv[1] == "api":
api_request(os.getenv("GITHUB_API_TOKEN"))
write_readme()
exit(0)
else:
print(f"error: unknown flag: {sys.argv[1]}")
exit(1)
except Exception as ex:
print(f"error: {ex}")
exit(1)