LysandreJik commited on
Commit
cef1f1e
1 Parent(s): d2889eb

Week over week + exclude org members + cumulated

Browse files
Files changed (3) hide show
  1. app.py +76 -49
  2. index.js +81 -11
  3. style.css +13 -0
app.py CHANGED
@@ -27,9 +27,24 @@ def _range(e):
27
  global val
28
  e['range'] = val
29
  val += 1
 
 
 
 
 
 
30
  return e
31
 
32
 
 
 
 
 
 
 
 
 
 
33
  stars = {}
34
  for k, v in datasets['stars'].items():
35
  stars[k] = v.map(_range)
@@ -39,6 +54,8 @@ issues = {}
39
  for k, v in datasets['issues'].items():
40
  issues[k] = v.map(_range)
41
  val = 0
 
 
42
 
43
  datasets['stars'] = DatasetDict(**stars)
44
  datasets['issues'] = DatasetDict(**issues)
@@ -69,6 +86,17 @@ def running_mean(x, N, total_length=-1):
69
  return np.pad(cumsum[N:] - cumsum[:-N], (to_pad, 0)) / float(N)
70
 
71
 
 
 
 
 
 
 
 
 
 
 
 
72
  class RequestHandler(SimpleHTTPRequestHandler):
73
  def do_GET(self):
74
  print(self.path)
@@ -86,28 +114,16 @@ class RequestHandler(SimpleHTTPRequestHandler):
86
  if len(v) < len(dataset_with_most_splits):
87
  warnings.extend(f"The {k} dataset does not contain all splits. Missing: {dataset_with_most_splits - v}")
88
 
89
- self.send_response(200)
90
- self.send_header("Content-Type", "application/json")
91
- self.end_headers()
92
-
93
  # TODO: Send and display warnings
94
  dataset_with_most_splits = list(dataset_with_most_splits)
95
  dataset_with_most_splits.sort()
96
- self.wfile.write(json.dumps(list(dataset_with_most_splits)).encode("utf-8"))
97
 
98
- return SimpleHTTPRequestHandler
99
 
100
  if self.path.startswith("/retrievePipInstalls"):
101
- url = urlparse(self.path)
102
- query = parse_qs(url.query)
103
- library_names = query.get("input", None)[0]
104
- library_names = library_names.split(',')
105
-
106
- if 'Cumulated' in library_names:
107
- dataset_keys = {k: set(v.keys()) for k, v in datasets.items()}
108
- dataset_with_most_splits = max([d for d in dataset_keys.values()], key=len)
109
- library_names = list(dataset_with_most_splits)
110
 
 
111
  returned_values = {}
112
  for library_name in library_names:
113
  for i in datasets['pip'][library_name]:
@@ -136,31 +152,29 @@ class RequestHandler(SimpleHTTPRequestHandler):
136
  output = {l: [k[l] for k in returned_values.values()] for l in library_names}
137
  output['day'] = list(returned_values.keys())
138
 
139
- self.send_response(200)
140
- self.send_header("Content-Type", "application/json")
141
- self.end_headers()
142
-
143
- self.wfile.write(json.dumps(output).encode("utf-8"))
144
-
145
- return SimpleHTTPRequestHandler
146
 
147
  if self.path.startswith("/retrieveStars"):
148
- url = urlparse(self.path)
149
- query = parse_qs(url.query)
150
- library_names = query.get("input", None)[0]
151
- library_names = library_names.split(',')
152
-
153
  returned_values = {}
154
  dataset_dict = datasets['stars']
 
155
 
156
  for library_name in library_names:
157
  dataset = dataset_dict[library_name]
158
 
 
 
159
  for i in dataset:
 
 
160
  if i['dates'] in returned_values:
161
- returned_values[i['dates']][library_name] = i['range']
162
  else:
163
- returned_values[i['dates']] = {library_name: i['range']}
 
 
 
164
 
165
  returned_values = collections.OrderedDict(sorted(returned_values.items()))
166
  returned_values = link_values(library_names, returned_values)
@@ -168,33 +182,40 @@ class RequestHandler(SimpleHTTPRequestHandler):
168
  output['day'] = list(returned_values.keys())[::-1]
169
 
170
  # Trim down to a smaller number of points.
171
- output = {k: [v for i, v in enumerate(value) if i % int(len(value) / 100) == 0] for k, value in output.items()}
172
-
173
- self.send_response(200)
174
- self.send_header("Content-Type", "application/json")
175
- self.end_headers()
176
 
177
- self.wfile.write(json.dumps(output).encode("utf-8"))
178
 
179
- return SimpleHTTPRequestHandler
180
 
181
  if self.path.startswith("/retrieveIssues"):
182
- url = urlparse(self.path)
183
- query = parse_qs(url.query)
184
- library_names = query.get("input", None)[0]
185
- library_names = library_names.split(',')
186
 
187
  returned_values = {}
188
  dataset_dict = datasets['issues']
 
189
 
190
  for library_name in library_names:
191
  dataset = dataset_dict[library_name]
192
 
 
 
193
  for i in dataset:
 
 
 
194
  if i['dates'] in returned_values:
195
- returned_values[i['dates']][library_name] = i['range']
196
  else:
197
- returned_values[i['dates']] = {library_name: i['range']}
 
 
 
198
 
199
  returned_values = collections.OrderedDict(sorted(returned_values.items()))
200
  returned_values = link_values(library_names, returned_values)
@@ -202,17 +223,23 @@ class RequestHandler(SimpleHTTPRequestHandler):
202
  output['day'] = list(returned_values.keys())[::-1]
203
 
204
  # Trim down to a smaller number of points.
205
- output = {k: [v for i, v in enumerate(value) if i % int(len(value) / 100) == 0] for k, value in output.items()}
 
 
 
206
 
207
- self.send_response(200)
208
- self.send_header("Content-Type", "application/json")
209
- self.end_headers()
210
 
211
- self.wfile.write(json.dumps(output).encode("utf-8"))
212
 
213
- return SimpleHTTPRequestHandler
 
 
 
214
 
215
- return SimpleHTTPRequestHandler.do_GET(self)
 
 
216
 
217
 
218
  server = ThreadingHTTPServer(("", 7860), RequestHandler)
 
27
  global val
28
  e['range'] = val
29
  val += 1
30
+
31
+ current_date = datetime.strptime(e['dates'], "%Y-%m-%dT%H:%M:%SZ")
32
+ first_date = datetime.fromtimestamp(1)
33
+ week = abs(current_date - first_date).days // 7
34
+ e['week'] = week
35
+
36
  return e
37
 
38
 
39
+ def _ignore_org_members(e):
40
+ global val
41
+ e['range_non_org'] = val
42
+
43
+ if e['type']['authorAssociation'] != 'MEMBER':
44
+ val += 1
45
+
46
+ return e
47
+
48
  stars = {}
49
  for k, v in datasets['stars'].items():
50
  stars[k] = v.map(_range)
 
54
  for k, v in datasets['issues'].items():
55
  issues[k] = v.map(_range)
56
  val = 0
57
+ issues[k] = issues[k].map(_ignore_org_members)
58
+ val = 0
59
 
60
  datasets['stars'] = DatasetDict(**stars)
61
  datasets['issues'] = DatasetDict(**issues)
 
86
  return np.pad(cumsum[N:] - cumsum[:-N], (to_pad, 0)) / float(N)
87
 
88
 
89
+ def parse_name_and_options(path):
90
+ url = urlparse(path)
91
+ query = parse_qs(url.query)
92
+ library_names = query.get("input", None)[0]
93
+ library_names = library_names.split(',')
94
+ options = query.get("options", None)[0]
95
+ options = options.split(',')
96
+
97
+ return library_names, options
98
+
99
+
100
  class RequestHandler(SimpleHTTPRequestHandler):
101
  def do_GET(self):
102
  print(self.path)
 
114
  if len(v) < len(dataset_with_most_splits):
115
  warnings.extend(f"The {k} dataset does not contain all splits. Missing: {dataset_with_most_splits - v}")
116
 
 
 
 
 
117
  # TODO: Send and display warnings
118
  dataset_with_most_splits = list(dataset_with_most_splits)
119
  dataset_with_most_splits.sort()
 
120
 
121
+ return self.response(list(dataset_with_most_splits))
122
 
123
  if self.path.startswith("/retrievePipInstalls"):
124
+ library_names, options = parse_name_and_options(self.path)
 
 
 
 
 
 
 
 
125
 
126
+ if '1' in options:
127
  returned_values = {}
128
  for library_name in library_names:
129
  for i in datasets['pip'][library_name]:
 
152
  output = {l: [k[l] for k in returned_values.values()] for l in library_names}
153
  output['day'] = list(returned_values.keys())
154
 
155
+ return self.response(output)
 
 
 
 
 
 
156
 
157
  if self.path.startswith("/retrieveStars"):
158
+ library_names, options = parse_name_and_options(self.path)
 
 
 
 
159
  returned_values = {}
160
  dataset_dict = datasets['stars']
161
+ week_over_week = '1' in options
162
 
163
  for library_name in library_names:
164
  dataset = dataset_dict[library_name]
165
 
166
+ last_value = 0
167
+ last_week = dataset[0]['week']
168
  for i in dataset:
169
+ if week_over_week and last_week == i['week']:
170
+ continue
171
  if i['dates'] in returned_values:
172
+ returned_values[i['dates']][library_name] = i['range'] - last_value
173
  else:
174
+ returned_values[i['dates']] = {library_name: i['range'] - last_value}
175
+
176
+ last_value = i['range'] if week_over_week else 0
177
+ last_week = i['week']
178
 
179
  returned_values = collections.OrderedDict(sorted(returned_values.items()))
180
  returned_values = link_values(library_names, returned_values)
 
182
  output['day'] = list(returned_values.keys())[::-1]
183
 
184
  # Trim down to a smaller number of points.
185
+ if len(output) > 100:
186
+ output = {
187
+ k: [v for i, v in enumerate(value) if i % int(len(value) / 100) == 0] for k, value in output.items()
188
+ }
 
189
 
190
+ return self.response(output)
191
 
 
192
 
193
  if self.path.startswith("/retrieveIssues"):
194
+ library_names, options = parse_name_and_options(self.path)
195
+
196
+ exclude_org_members = '1' in options
197
+ week_over_week = '2' in options
198
 
199
  returned_values = {}
200
  dataset_dict = datasets['issues']
201
+ range_id = 'range' if not exclude_org_members else 'range_non_org'
202
 
203
  for library_name in library_names:
204
  dataset = dataset_dict[library_name]
205
 
206
+ last_value = 0
207
+ last_week = dataset[0]['week']
208
  for i in dataset:
209
+ if week_over_week and last_week == i['week']:
210
+ continue
211
+
212
  if i['dates'] in returned_values:
213
+ returned_values[i['dates']][library_name] = i[range_id] - last_value
214
  else:
215
+ returned_values[i['dates']] = {library_name: i[range_id] - last_value}
216
+
217
+ last_value = i[range_id] if week_over_week else 0
218
+ last_week = i['week']
219
 
220
  returned_values = collections.OrderedDict(sorted(returned_values.items()))
221
  returned_values = link_values(library_names, returned_values)
 
223
  output['day'] = list(returned_values.keys())[::-1]
224
 
225
  # Trim down to a smaller number of points.
226
+ if len(output) > 100:
227
+ output = {
228
+ k: [v for i, v in enumerate(value) if i % int(len(value) / 100) == 0] for k, value in output.items()
229
+ }
230
 
231
+ return self.response(output)
 
 
232
 
233
+ return SimpleHTTPRequestHandler.do_GET(self)
234
 
235
+ def response(self, output):
236
+ self.send_response(200)
237
+ self.send_header("Content-Type", "application/json")
238
+ self.end_headers()
239
 
240
+ self.wfile.write(json.dumps(output).encode("utf-8"))
241
+
242
+ return SimpleHTTPRequestHandler
243
 
244
 
245
  server = ThreadingHTTPServer(("", 7860), RequestHandler)
index.js CHANGED
@@ -13,6 +13,61 @@ const load = () => {
13
  return l0
14
  }
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  let charts = [];
17
 
18
  const createButton = (title, libraries, methods) => {
@@ -21,16 +76,20 @@ const createButton = (title, libraries, methods) => {
21
  button.onclick = async () => {
22
  document.getElementById('pip-graph').innerHTML = ''
23
  document.getElementById('star-graph').innerHTML = ''
 
24
  const e = load()
25
  document.body.appendChild(e)
26
  const selectedLibraries = libraries.filter(e => document.querySelector(`#${e}Checkbox`).checked);
 
 
 
27
  if (charts.length !== 0) {
28
  for (const chart of charts) {
29
  chart.destroy()
30
  }
31
  }
32
  for (const method of methods()) {
33
- charts.push(await method(selectedLibraries))
34
  }
35
  document.body.removeChild(e)
36
  };
@@ -75,8 +134,11 @@ const initialize = async () => {
75
  librarySelector.appendChild(div)
76
  }
77
 
78
- for (const element of ['pip', 'stars', 'issues']) {
79
  const div = document.createElement('div');
 
 
 
80
  const checkBox = document.createElement('input');
81
  checkBox.type = 'checkbox'
82
  checkBox.id = `${element}CheckboxGraph`;
@@ -91,8 +153,13 @@ const initialize = async () => {
91
  graphSelector.appendChild(div)
92
  }
93
 
 
 
 
 
 
94
  const fetchButton = createButton('Fetch', inferJson, () => {
95
- const graphNames = ['pip', 'stars', 'issues'].filter(e => document.querySelector(`#${e}CheckboxGraph`).checked);
96
  const graphs = []
97
 
98
  if (graphNames.includes('pip'))
@@ -101,7 +168,7 @@ const initialize = async () => {
101
  if (graphNames.includes('stars'))
102
  graphs.push(retrieveStars)
103
 
104
- if (graphNames.includes('issues'))
105
  graphs.push(retrieveIssues)
106
 
107
  return graphs
@@ -109,8 +176,9 @@ const initialize = async () => {
109
  selectorSubmit.appendChild(fetchButton);
110
  };
111
 
112
- const retrievePipInstalls = async (libraryNames) => {
113
- const inferResponse = await fetch(`retrievePipInstalls?input=${libraryNames}`);
 
114
  const inferJson = await inferResponse.json();
115
  const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#0000FF', '#F090F0', '#90F0F0', '#F0F090']
116
 
@@ -159,8 +227,9 @@ const retrievePipInstalls = async (libraryNames) => {
159
  return myChart;
160
  };
161
 
162
- const retrieveStars = async (libraryNames) => {
163
- const inferResponse = await fetch(`retrieveStars?input=${libraryNames}`);
 
164
  const inferJson = await inferResponse.json();
165
  const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#0000FF', '#F090F0', '#90F0F0', '#F0F090']
166
 
@@ -209,8 +278,9 @@ const retrieveStars = async (libraryNames) => {
209
  return myChart;
210
  };
211
 
212
- const retrieveIssues = async (libraryNames) => {
213
- const inferResponse = await fetch(`retrieveIssues?input=${libraryNames}`);
 
214
  const inferJson = await inferResponse.json();
215
  const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#0000FF', '#F090F0', '#90F0F0', '#F0F090']
216
 
@@ -252,7 +322,7 @@ const retrieveIssues = async (libraryNames) => {
252
  plugins: {
253
  title: {
254
  display: true,
255
- text: 'Number of issues, PRs, and comments on these'
256
  }
257
  }
258
  }
 
13
  return l0
14
  }
15
 
16
+ const getCheckedOptions = () => {
17
+ const options = Array.from(document.querySelectorAll('.option-div'))
18
+ .map(e => Array.from(e.children)
19
+ .filter(e => e.nodeName == 'DIV'))
20
+ .filter(e => e.length)
21
+ .flat()
22
+ .map(e => e.id)
23
+ .filter(e => document.querySelector(`#${e}-checkbox`).checked)
24
+
25
+ const optionsDict = {}
26
+ for (let option of options) {
27
+ const key = option.split('-option-')[0]
28
+ const value = option.split('-option-')[1]
29
+
30
+ if (key in optionsDict)
31
+ optionsDict[key].push(value)
32
+ else
33
+ optionsDict[key] = [value]
34
+ }
35
+
36
+ console.log('dict', optionsDict)
37
+
38
+ return optionsDict;
39
+ }
40
+
41
+ const addOption = (category, optionName) => {
42
+ /* Options for the issue div */
43
+ const issueDiv = document.getElementById(`${category}Div`);
44
+ const div = document.createElement('div')
45
+
46
+ let found = false;
47
+ let optionNumber = 0;
48
+ while (!found && ++optionNumber < 100) {
49
+ let previousOption = document.getElementById(`${category}-option-${optionNumber}`);
50
+ console.log(previousOption)
51
+ found = previousOption === null;
52
+ }
53
+
54
+ div.id = `${category}-option-${optionNumber}`;
55
+ issueDiv.appendChild(div);
56
+
57
+ const checkBox = document.createElement('input');
58
+ checkBox.type = 'checkbox'
59
+ checkBox.id = `${category}-option-${optionNumber}-checkbox`
60
+
61
+ const checkBoxLabel = document.createElement('label');
62
+ const labelSpan = document.createElement('span')
63
+ labelSpan.textContent = optionName;
64
+ checkBoxLabel.appendChild(checkBox)
65
+ checkBoxLabel.appendChild(labelSpan)
66
+ div.appendChild(checkBoxLabel)
67
+
68
+ return optionNumber
69
+ }
70
+
71
  let charts = [];
72
 
73
  const createButton = (title, libraries, methods) => {
 
76
  button.onclick = async () => {
77
  document.getElementById('pip-graph').innerHTML = ''
78
  document.getElementById('star-graph').innerHTML = ''
79
+ document.getElementById('issue-graph').innerHTML = ''
80
  const e = load()
81
  document.body.appendChild(e)
82
  const selectedLibraries = libraries.filter(e => document.querySelector(`#${e}Checkbox`).checked);
83
+
84
+ const relevantOptions = getCheckedOptions();
85
+
86
  if (charts.length !== 0) {
87
  for (const chart of charts) {
88
  chart.destroy()
89
  }
90
  }
91
  for (const method of methods()) {
92
+ charts.push(await method(selectedLibraries, relevantOptions))
93
  }
94
  document.body.removeChild(e)
95
  };
 
134
  librarySelector.appendChild(div)
135
  }
136
 
137
+ for (const element of ['pip', 'stars', 'issue']) {
138
  const div = document.createElement('div');
139
+ div.classList.add('option-div')
140
+ div.id = `${element}Div`;
141
+
142
  const checkBox = document.createElement('input');
143
  checkBox.type = 'checkbox'
144
  checkBox.id = `${element}CheckboxGraph`;
 
153
  graphSelector.appendChild(div)
154
  }
155
 
156
+ addOption('pip', "Cumulated");
157
+ addOption('issue', "Exclude org members");
158
+ addOption('issue', "Week over week");
159
+ addOption('stars', "Week over week");
160
+
161
  const fetchButton = createButton('Fetch', inferJson, () => {
162
+ const graphNames = ['pip', 'stars', 'issue'].filter(e => document.querySelector(`#${e}CheckboxGraph`).checked);
163
  const graphs = []
164
 
165
  if (graphNames.includes('pip'))
 
168
  if (graphNames.includes('stars'))
169
  graphs.push(retrieveStars)
170
 
171
+ if (graphNames.includes('issue'))
172
  graphs.push(retrieveIssues)
173
 
174
  return graphs
 
176
  selectorSubmit.appendChild(fetchButton);
177
  };
178
 
179
+ const retrievePipInstalls = async (libraryNames, options) => {
180
+ const relevantOptions = options['pip']
181
+ const inferResponse = await fetch(`retrievePipInstalls?input=${libraryNames}&options=${relevantOptions}`);
182
  const inferJson = await inferResponse.json();
183
  const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#0000FF', '#F090F0', '#90F0F0', '#F0F090']
184
 
 
227
  return myChart;
228
  };
229
 
230
+ const retrieveStars = async (libraryNames, options) => {
231
+ const relevantOptions = options['stars']
232
+ const inferResponse = await fetch(`retrieveStars?input=${libraryNames}&options=${relevantOptions}`);
233
  const inferJson = await inferResponse.json();
234
  const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#0000FF', '#F090F0', '#90F0F0', '#F0F090']
235
 
 
278
  return myChart;
279
  };
280
 
281
+ const retrieveIssues = async (libraryNames, options) => {
282
+ const relevantOptions = options['issue']
283
+ const inferResponse = await fetch(`retrieveIssues?input=${libraryNames}&options=${relevantOptions}`);
284
  const inferJson = await inferResponse.json();
285
  const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#0000FF', '#F090F0', '#90F0F0', '#F0F090']
286
 
 
322
  plugins: {
323
  title: {
324
  display: true,
325
+ text: 'Number of issue, PRs, and comments on these'
326
  }
327
  }
328
  }
style.css CHANGED
@@ -39,6 +39,19 @@ button:hover {
39
  width: 100%;
40
  }
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  .submit {
43
  margin-bottom: 50px;
44
  }
 
39
  width: 100%;
40
  }
41
 
42
+ .option-div {
43
+ border-radius: 3px;
44
+ border-color: rgb(180, 180, 200);
45
+ border-style: solid;
46
+ border-width: 2px 4px 4px 2px;
47
+ margin: 14px 0;
48
+ padding: 5px;
49
+ }
50
+
51
+ .option-div > div {
52
+ margin-left: 20px;
53
+ }
54
+
55
  .submit {
56
  margin-bottom: 50px;
57
  }