@@ -211,7 +211,7 @@ def _library_image(library):
211
211
logo = "_static/images/occ-logo.png"
212
212
elif library == "petsc4py" :
213
213
logo = "_static/images/petsc4py-logo.png"
214
- elif library == "pybind11" or library .startswith ("pybind11 (" ):
214
+ elif library == "pybind11" or library .startswith ("pybind11 & nanobind (" ):
215
215
logo = "_static/images/pybind11-logo.png"
216
216
elif library == "RBniCS" :
217
217
logo = "_static/images/rbnics-logo.png"
@@ -226,6 +226,17 @@ def _library_image(library):
226
226
class Stats (Directive ):
227
227
228
228
def run (self ):
229
+ def get_week_representation (day : datetime .date ) -> str :
230
+ """
231
+ Get the week representation year-week number.
232
+
233
+ Uses day.isocalendar() rather than day.strftime("%Y-%W") to prevent
234
+ wrong identification of the (week number, year) pair on the first week
235
+ of the year, see the documentation of datetime.date.isocalendar().
236
+ """
237
+ isocalendar_tuple = day .isocalendar ()
238
+ return f"{ isocalendar_tuple .year } -{ isocalendar_tuple .week } "
239
+
229
240
# Get release download stats from file, and convert it to a plot
230
241
stats = pd .read_csv ("_stats/stats.csv" )
231
242
first_day = None
@@ -237,13 +248,13 @@ def run(self):
237
248
first_day = day
238
249
else :
239
250
first_day = min (first_day , day )
240
- week = day . strftime ( "%Y-%W" )
251
+ week = get_week_representation ( day )
241
252
if week not in week_to_headers :
242
253
week_to_headers [week ] = list ()
243
254
week_to_headers [week ].append (header )
244
255
weeks = pd .date_range (
245
256
start = first_day - timedelta (days = first_day .weekday ()), end = datetime .now (), freq = "W-MON" ).tolist ()[:- 1 ]
246
- weeks_str = [week . strftime ( "%Y-%W" ) for week in weeks ]
257
+ weeks_str = [get_week_representation ( week ) for week in weeks ]
247
258
weekly_stats = pd .DataFrame (0 , columns = list (packages .keys ()), index = weeks_str )
248
259
for package in weekly_stats .columns :
249
260
condition = stats .package .str .fullmatch (package )
@@ -253,18 +264,25 @@ def run(self):
253
264
weekly_stats .loc [week , package ] = max (stats_package [header ] for header in headers )
254
265
if len (weeks ) > 1 :
255
266
fig = go .Figure ()
267
+ max_downloads = 0
256
268
for package in weekly_stats .columns :
257
269
weekly_stats_package = weekly_stats [package ]
270
+ weekly_stats_package_diff = weekly_stats_package .diff ().to_numpy ()
258
271
fig .add_scatter (
259
- x = weeks [1 :], y = np . diff ( weekly_stats_package ) , mode = "lines+markers" ,
272
+ x = weeks [1 :], y = weekly_stats_package_diff , mode = "lines+markers" ,
260
273
name = packages [package ]["title" ])
274
+ if len (weeks ) > 13 :
275
+ max_downloads = max (max_downloads , np .max (weekly_stats_package_diff [- 14 :]))
276
+ else :
277
+ max_downloads = max (max_downloads , np .max (weekly_stats_package_diff ))
261
278
milliseconds_in_one_week = 604800000
262
279
fig .update_xaxes (tickformat = "%Y-%W" , dtick = milliseconds_in_one_week )
263
280
if len (weeks ) > 13 :
264
281
fig .update_xaxes (
265
282
tick0 = weeks [- 14 ], range = [weeks [- 14 ] - timedelta (days = 3 ), weeks [- 1 ] + timedelta (days = 3 )])
266
283
else :
267
284
fig .update_xaxes (tick0 = weeks [0 ])
285
+ fig .update_yaxes (tick0 = - 0.1 * max_downloads , range = [- 0.1 * max_downloads , 1.1 * max_downloads ])
268
286
html_buffer = io .StringIO ()
269
287
fig .write_html (html_buffer , full_html = False )
270
288
return [nodes .raw (text = html_buffer .getvalue (), format = "html" )]
@@ -275,22 +293,22 @@ def on_build_finished(app, exc):
275
293
if exc is None and app .builder .format == "html" :
276
294
# Unescape at symbol
277
295
subprocess .run (
278
- "find " + str (app .outdir ) + " -type f -not -path '*/\.git/*' -exec sed -i 's/%40/@/g' {} +" ,
296
+ "find " + str (app .outdir ) + r " -type f -not -path '*/\.git/*' -exec sed -i 's/%40/@/g' {} +" ,
279
297
shell = True )
280
298
subprocess .run ( # undo incorrect escape in plotly js
281
299
"sed -i 's/t@0=/t%400=/g' " + str (app .outdir ) + "/packages.html" ,
282
300
shell = True )
283
301
# Mark current page as active
284
302
subprocess .run (
285
- "find " + str (app .outdir ) + " -type f -not -path '*/\.git/*' -exec sed -i 's/"
303
+ "find " + str (app .outdir ) + r " -type f -not -path '*/\.git/*' -exec sed -i 's/"
286
304
+ '<li class="md-tabs__item"><a href="#" class="md-tabs__link">'
287
305
+ "/"
288
306
+ '<li class="md-tabs__item md-tabs__item_current"><a href="#" class="md-tabs__link">'
289
307
+ "/g' {} +" ,
290
308
shell = True )
291
309
# Disable going to submenus on mobile
292
310
subprocess .run (
293
- "find " + str (app .outdir ) + " -type f -not -path '*/\.git/*' -exec sed -i 's/"
311
+ "find " + str (app .outdir ) + r " -type f -not -path '*/\.git/*' -exec sed -i 's/"
294
312
+ 'id="__toc"'
295
313
+ "/"
296
314
+ 'id="__toc_disabled"'
0 commit comments