forked from WandrilleD/track_multiple_repos_traffic
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathscan_multiple_github_repo_traffic.py
More file actions
299 lines (235 loc) · 9.05 KB
/
Copy pathscan_multiple_github_repo_traffic.py
File metadata and controls
299 lines (235 loc) · 9.05 KB
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import os
import sys
from datetime import timedelta, datetime
from github import Github, Auth
def write_table( data , repo_list , file = sys.stdout ):
"""writes view/clone data as a csv file
data is expected to be a dictionary
whose keys are datetime
and values are dictionaries
whose keys are repo name
and values are corresponding value (eg, number of views for a given date for a given repo)
"""
print( "date" , *repo_list , sep = ',' , file=file )
for d in data:
print( d , *[data[d][r] for r in repo_list ] , sep = ',' , file=file )
def read_table( file ):
"""
reads a csv file and returns it as a dictionary
whose keys are datetime
and values are dictionaries
whose keys are repo name
and values are corresponding value (eg, number of views for a given date for a given repo)
"""
header = file.readline().strip()
repo_list = header.split(',')[1:]
data = {}
for l in file:
sl = l.strip().split(',')
date = datetime.fromisoformat( sl[0] )
data[ date ] = {}
for i,count in enumerate( sl[1:] ):
data[ date ][ repo_list[i] ] = count
return data
def get_metrics( repo , github_object , raise_error = True):
""" given 1 repository name and a github object, gather views and clones data.
returns the data in a dictionary whose keys are 'view_count','view_unique','clone_count', or 'clone_unique'
and whose values are dictionaries whose keys are datetime and value are the corresponding value (ie, number of views, or clone,..)
If the raise_error argument is False, if the code fails to gather the repo data (likely because it lacks authorization) it returns the expected object without data
Otherwise the thrown error is raised
"""
repo = github_object.get_repo( repo )
data = {}
data['view_count'] = {}
data['view_unique'] = {}
data['clone_count'] = {}
data['clone_unique'] = {}
try:
views = repo.get_views_traffic()
for v in views.views:
data['view_count'][v.timestamp] = v.count
data['view_unique'][v.timestamp] = v.uniques
clones = repo.get_clones_traffic()
for c in clones.clones:
data['clone_count'][c.timestamp] = c.count
data['clone_unique'][c.timestamp] = c.uniques
except Exception as e:
if raise_error:
raise e
return data
def get_referrers_and_paths( repo , github_object , raise_error = True):
""" given 1 repository name and a github object, gather top referrers and popular paths.
returns the data in a dictionary with keys 'referrers' and 'paths'
each containing a list of dictionaries with the aggregated data over the last 14 days
If the raise_error argument is False, if the code fails to gather the repo data (likely because it lacks authorization) it returns empty lists
Otherwise the thrown error is raised
"""
repo = github_object.get_repo( repo )
data = {}
data['referrers'] = []
data['paths'] = []
try:
# Get top 10 referral sources (aggregated over last 14 days)
referrers = repo.get_top_referrers()
for r in referrers:
data['referrers'].append({
'referrer': r.referrer,
'count': r.count,
'uniques': r.uniques
})
# Get top 10 popular paths (aggregated over last 14 days)
paths = repo.get_top_paths()
for p in paths:
data['paths'].append({
'path': p.path,
'title': p.title,
'count': p.count,
'uniques': p.uniques
})
except Exception as e:
if raise_error:
raise e
return data
def complement_data_structure( min_date, max_date, repo_list, data = {} ):
"""creates or complement data structure to store 1 metric
data is expected to be a dictionary
whose keys are datetime
and values are dictionaries
whose keys are repo name
and values are corresponding value (eg, number of views for a given date for a given repo)
"""
if len(data)>0: ## there is already data
# -> update minimum date
min_date = min(min_date , *(data.keys()) )
# -> update maximum date
max_date = max(max_date , *(data.keys()) )
# -> add repos data if they are absent. initialize their value to NA
for k in data:
for r in repo_list:
if not r in data[k]:
data[k][r] = "NA"
## making sure we have data for all days. initializing all at 0
delta = timedelta(days=1)
current = min_date
while current <= max_date:
if not current in data: # date absent from the current data -> initialize all at 0
data[current] = { r:0 for r in repo_list }
current += delta
return data
# using an access token
auth = Auth.Token(os.environ["TRAFFIC_ACTION_TOKEN"])
# Public Web Github
g = Github(auth=auth)
repo_list_file = sys.argv[1]
## files for view_count, view_unique, clone_count, clone_unique
files = { 'view_count' : sys.argv[2],
'view_unique' : sys.argv[3],
'clone_count' : sys.argv[4],
'clone_unique' : sys.argv[5]}
## files for referrers and paths (if provided)
referrers_file = sys.argv[6] if len(sys.argv) > 6 else None
paths_file = sys.argv[7] if len(sys.argv) > 7 else None
## reading the repo list
repo_list = []
with open(repo_list_file) as IN:
for l in IN:
repo_list.append( l.strip() )
## reading the data we already have
pre_data = {}
for k in files:
pre_data[k] = {}
if os.path.exists(files[k]):
with open(files[k]) as IN:
pre_data[k] = read_table(IN)
## gathering the data from github
raw_data = {}
referrers_data = {}
paths_data = {}
for r in repo_list:
raw_data[r] = get_metrics( repo = r,
github_object = g,
raise_error = False )
# Get referrers and paths if output files are specified
if referrers_file or paths_file:
ref_path_data = get_referrers_and_paths( repo = r,
github_object = g,
raise_error = False )
referrers_data[r] = ref_path_data['referrers']
paths_data[r] = ref_path_data['paths']
## determining the time window of the data
all_dates = set()
for r,data in raw_data.items():
for data2 in data.values():
all_dates.update( data2.keys() )
min_date , max_date = min(all_dates) , max(all_dates)
## adding the new dates to the data structure
data = {}
for k in pre_data:
data[k] = complement_data_structure( min_date, max_date,
repo_list=repo_list,
data = pre_data[k] )
## adding the new data
for k in pre_data:
for repo in raw_data:
for date,value in raw_data[repo][k].items():
data[k][date][repo] = value
# ensuring folders containing the output files are created
for k in data:
folder = k.rpartition("/")[0]
if folder != '' and folder != '.':
if not os.path.exists(folder):
os.makedirs(folder)
## writing data to files
for k in data:
with open( files[k],'w') as OUT:
write_table( data[k] , repo_list , file = OUT )
## writing referrers and paths data (snapshot with current timestamp)
import json
if referrers_file and referrers_data:
current_time = datetime.now()
# Read existing data if file exists
existing_referrers = []
if os.path.exists(referrers_file):
with open(referrers_file, 'r') as IN:
try:
existing_referrers = json.load(IN)
except:
existing_referrers = []
# Append new snapshot
snapshot = {
'timestamp': current_time.isoformat(),
'data': referrers_data
}
existing_referrers.append(snapshot)
# Ensure folder exists
folder = referrers_file.rpartition("/")[0]
if folder != '' and folder != '.':
if not os.path.exists(folder):
os.makedirs(folder)
# Write to file
with open(referrers_file, 'w') as OUT:
json.dump(existing_referrers, OUT, indent=2)
if paths_file and paths_data:
current_time = datetime.now()
# Read existing data if file exists
existing_paths = []
if os.path.exists(paths_file):
with open(paths_file, 'r') as IN:
try:
existing_paths = json.load(IN)
except:
existing_paths = []
# Append new snapshot
snapshot = {
'timestamp': current_time.isoformat(),
'data': paths_data
}
existing_paths.append(snapshot)
# Ensure folder exists
folder = paths_file.rpartition("/")[0]
if folder != '' and folder != '.':
if not os.path.exists(folder):
os.makedirs(folder)
# Write to file
with open(paths_file, 'w') as OUT:
json.dump(existing_paths, OUT, indent=2)