-
-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathroutes.py
356 lines (275 loc) · 10.5 KB
/
routes.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
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
from flask import redirect, request, jsonify, abort, send_from_directory
import re
from . import app, talisman, render_template, url_for
from . import reports as report_util
from . import techreport as tech_report_util
from . import faq as faq_util
@app.route("/")
def index():
return render_template(
"index.html",
reports=report_util.get_reports(),
featured_reports=report_util.get_featured_reports(),
faq=faq_util,
)
@app.route("/about", strict_slashes=False)
def about():
if request.base_url[-1] == "/":
return redirect("/about"), 301
return render_template("about.html", reports=report_util.get_reports())
@app.route("/faq", strict_slashes=False)
def faq():
if request.base_url[-1] == "/":
return redirect("/faq"), 301
return render_template("faq.html", reports=report_util.get_reports(), faq=faq_util)
# A public JSON endpoint to get info about a given metric.
@app.route("/metric.json")
def metric():
metric_id = request.args.get("id")
if not metric_id:
abort(jsonify(status=400, message="id parameter required"))
metric = report_util.get_metric(metric_id)
# A metric has a histogram if it is not explicitly disabled.
has_histogram = metric and metric.get("histogram", {}).get("enabled", True)
latest = (
report_util.get_latest_date(metric_id) if metric and has_histogram else None
)
return jsonify(status=200, metric=metric, latest=latest)
@app.route("/reports", strict_slashes=False)
def reports():
if request.base_url[-1] == "/":
return redirect("/reports"), 301
all_reports = report_util.get_reports()
# Return as JSON if requested.
if get_format(request) == "json":
return jsonify(status=200, reports=all_reports)
return render_template("reports.html", reports=all_reports)
@app.route("/reports/techreport/<page_id>", strict_slashes=False)
def techreportlanding(page_id):
# Needed for the header dropdown
all_reports = report_util.get_reports()
# Get the configuration for the tech report
tech_report = tech_report_util.get_report()
# Get the settings for the current page
active_tech_report = tech_report.get("pages").get(page_id)
# Add the technologies requested in the URL to the filters
# Use the default configured techs as fallback
# Use ["ALL"] if there is nothing configured
requested_technologies = active_tech_report.get("config").get("default").get(
"app"
) or ["ALL"]
if request.args.get("tech"):
requested_technologies = request.args.get("tech").split(",")
# Get the filters
requested_geo = request.args.get("geo") or "ALL"
requested_rank = request.args.get("rank") or "ALL"
requested_category = request.args.get("category") or "ALL"
filters = {
"geo": requested_geo,
"rank": requested_rank,
"app": requested_technologies,
"category": requested_category,
}
active_tech_report["filters"] = filters
return render_template(
"techreport/%s.html" % page_id,
active_page=page_id,
tech_report_labels=tech_report.get("labels"),
tech_report_config=tech_report.get("config"),
tech_report_page=active_tech_report,
custom_navigation=True,
reports=all_reports,
)
@app.route("/reports/techreport/tech", strict_slashes=False)
def techreport():
# Needed for the header dropdown
all_reports = report_util.get_reports()
# Get the configuration for the tech report
tech_report = tech_report_util.get_report()
# Get the current page_id
requested_technologies = ["ALL"]
if request.args.get("tech"):
requested_technologies = request.args.get("tech").split(",")
if len(requested_technologies) > 1:
page_id = "comparison"
else:
page_id = "drilldown"
# Get the settings for the current page
active_tech_report = tech_report.get("pages").get(page_id)
# Add the technologies requested in the URL to the filters
# Use the default configured techs as fallback
# Use ["ALL"] if there is nothing configured
requested_technologies = active_tech_report.get("config").get("default").get(
"app"
) or ["ALL"]
if request.args.get("tech"):
requested_technologies = request.args.get("tech").split(",")
# Get the filters
requested_geo = request.args.get("geo") or "ALL"
requested_rank = request.args.get("rank") or "ALL"
requested_category = request.args.get("category") or "ALL"
filters = {
"geo": requested_geo,
"rank": requested_rank,
"app": requested_technologies,
"category": requested_category,
}
active_tech_report["filters"] = filters
return render_template(
"techreport/%s.html" % page_id,
active_page=page_id,
requested_page="technology",
tech_report_labels=tech_report.get("labels"),
tech_report_config=tech_report.get("config"),
tech_report_page=active_tech_report,
custom_navigation=True,
reports=all_reports,
)
@app.route("/reports/<report_id>", strict_slashes=False)
def report(report_id):
if request.base_url[-1] == "/":
return redirect("/reports/%s" % (report_id)), 301
report = report_util.get_report(report_id)
if not report:
abort(404)
report_url = report_util.get_report(report_id).get("url")
if report_url:
return redirect(report_url), 302
dates = report_util.get_dates()
if not dates: # pragma: no cover
abort(500)
min_date = report.get("minDate")
max_date = report.get("maxDate")
date_pattern = report.get("datePattern")
max_date_metric = report.get("maxDateMetric")
# TODO: If a report doesn't explicitly have a min/max date,
# but all of its metrics do, take the min/max of the metrics
# and set that as the report's implicit min/max date.
# Omit dates for which this report has no data.
if max_date_metric:
max_date = report_util.get_latest_date(max_date_metric)
if min_date:
dates = dates[: dates.index(min_date) + 1]
if max_date:
dates = dates[dates.index(max_date) :] # noqa: E203
if date_pattern:
date_pattern = re.compile(date_pattern)
dates = [d for d in dates if date_pattern.match(d)]
report["dates"] = dates
report["lenses"] = report_util.get_lenses()
start = request.args.get("start")
end = request.args.get("end")
# Canonicalize single-date formats.
if end and not start:
start, end = end, start
# Canonicalize aliases.
if start == "latest":
start = dates[0]
elif start == "earliest":
start = dates[-1]
if end == "latest":
end = dates[0]
elif end == "earliest":
end = dates[-1]
# This is longhand for the snapshot (histogram) view.
if start == end:
end = None
# This is shorthand for the trends (timeseries) view.
if not start and not end:
# The default date range is 72 crawls (3 years).
# May be shorter if the report's minimum date is more recent.
start = dates[min(72, len(dates) - 1)]
end = dates[0]
if start and start not in dates:
abort(400)
if end and end not in dates:
abort(400)
viz = (
report_util.VizTypes.HISTOGRAM
if (start and not end)
else report_util.VizTypes.TIMESERIES
)
if (
viz == report_util.VizTypes.TIMESERIES
and report.get("timeseries")
and not report.get("timeseries").get("enabled")
): # pragma: no cover
end = None
viz = report_util.VizTypes.HISTOGRAM
# The default for histograms should be the latest date.
if not request.args.get("start"):
start = dates[0]
lens_id = get_lens_id(request)
lens = report_util.get_lens(lens_id)
if lens:
report["lens"] = lens
report["view"] = get_report_view(report, request)
# Determine which metrics should be enabled for this report.
for metric in report["metrics"]:
# Get a list of reports that also contain this metric.
metric["similar_reports"] = report_util.get_similar_reports(
metric["id"], report_id
)
# Mark the lens used for this metric, if applicable.
if lens:
metric["lens"] = lens
metric[viz] = metric.get(viz, {})
enabled = metric[viz].get("enabled", True)
min_date = metric[viz].get("minDate", start)
max_date = metric[viz].get("maxDate", end)
# Disabled metrics should stay that way.
if not enabled:
continue
# Disable the metric if it start/end is outside of the min/max window.
enabled = start >= min_date
if max_date and enabled:
enabled = start <= max_date
if end and enabled:
enabled = end <= max_date
metric[viz]["enabled"] = enabled
script_root = url_for("report", report_id=report_id, _external=True)
# Return as JSON if requested.
if get_format(request) == "json":
return jsonify(status=200, report=report, start=start, end=end, viz=viz)
return render_template(
"report/%s.html" % viz,
viz=viz,
script_root=script_root,
reports=report_util.get_reports(),
report=report,
start=start,
end=end,
)
def get_lens_id(request):
host = request.host.split(".")
subdomain = len(host) > 2 and host[0] or ""
return request.args.get("lens") or subdomain
def get_report_view(report, request):
view = request.args.get("view")
return view if view in ("list", "grid") else report.get("view", "list")
def get_format(request):
return request.args.get("f")
@app.route("/robots.txt")
def static_from_root():
return send_from_directory(app.static_folder, request.path[1:])
@app.route("/favicon.ico")
def default_favicon():
return send_from_directory(app.static_folder, "img/favicon.ico")
@app.route("/sitemap.xml")
# Chrome and Safari use inline styles to display XMLs files.
# https://bugs.chromium.org/p/chromium/issues/detail?id=924962
# Override default CSP (including turning off nonce) to allow sitemap to display
@talisman(
content_security_policy={
"default-src": ["'self'"],
"script-src": ["'self'"],
"style-src": ["'unsafe-inline'"],
"img-src": ["'self'", "data:"],
},
content_security_policy_nonce_in=["script-src"],
)
def sitemap():
xml = render_template("sitemap.xml")
resp = app.make_response(xml)
resp.mimetype = "text/xml"
return resp