From 9029dc60f34fbfa05dd0903c5dd7e7626d462e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20G=C3=BCneli?= Date: Sun, 10 Nov 2024 01:21:21 +0100 Subject: [PATCH 1/3] Page Weight 2024 Queries (#3732) * Queries of page-weight 2024 * Fix linter errors * Fix wrong css bytes name * CWV trend SQL implemented * Fix linter issues * fix cwv and css request type calculation --------- Co-authored-by: burak.gueneli --- sql/2024/page-weight/bytes_per_type.sql | 25 ++++++ sql/2024/page-weight/compression-usage.sql | 22 +++++ sql/2024/page-weight/cwv_trend.sql | 86 +++++++++++++++++++ sql/2024/page-weight/facades-usage.sql | 22 +++++ sql/2024/page-weight/minified_css_usage.sql | 22 +++++ sql/2024/page-weight/minified_js_usage.sql | 22 +++++ sql/2024/page-weight/page_weight_trend.sql | 23 +++++ .../page-weight/request_type_distribution.sql | 25 ++++++ .../response_format_distribution.sql | 21 +++++ .../response_type_distribution.sql | 21 +++++ 10 files changed, 289 insertions(+) create mode 100644 sql/2024/page-weight/bytes_per_type.sql create mode 100644 sql/2024/page-weight/compression-usage.sql create mode 100644 sql/2024/page-weight/cwv_trend.sql create mode 100644 sql/2024/page-weight/facades-usage.sql create mode 100644 sql/2024/page-weight/minified_css_usage.sql create mode 100644 sql/2024/page-weight/minified_js_usage.sql create mode 100644 sql/2024/page-weight/page_weight_trend.sql create mode 100644 sql/2024/page-weight/request_type_distribution.sql create mode 100644 sql/2024/page-weight/response_format_distribution.sql create mode 100644 sql/2024/page-weight/response_type_distribution.sql diff --git a/sql/2024/page-weight/bytes_per_type.sql b/sql/2024/page-weight/bytes_per_type.sql new file mode 100644 index 00000000000..6d1e56419eb --- /dev/null +++ b/sql/2024/page-weight/bytes_per_type.sql @@ -0,0 +1,25 @@ +SELECT + percentile, + client, + is_root_page, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS total_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesHtml') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS html_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesJS') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS js_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesCss') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS css_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesImg') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS img_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesOther') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS other_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesHtmlDoc') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS html_doc_kbytes, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesFont') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS font_kbytes +FROM + `httparchive.all.pages`, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +WHERE + date = '2024-06-01' +GROUP BY + percentile, + client, + is_root_page +ORDER BY + client, + is_root_page, + percentile diff --git a/sql/2024/page-weight/compression-usage.sql b/sql/2024/page-weight/compression-usage.sql new file mode 100644 index 00000000000..60bf4a37e9e --- /dev/null +++ b/sql/2024/page-weight/compression-usage.sql @@ -0,0 +1,22 @@ +SELECT + client, + is_root_page, + COUNTIF(JSON_VALUE(lighthouse, '$.audits.uses-text-compression.score') IS NULL) AS null_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.uses-text-compression.score') AS FLOAT64) >= 0.9) AS pass_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.uses-text-compression.score') AS FLOAT64) < 0.9) AS fail_count, + COUNT(0) AS total, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.uses-text-compression.score') AS FLOAT64) >= 0.9) / COUNT(0) AS pct_pass, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.uses-text-compression.score') AS FLOAT64) < 0.9) / COUNT(0) AS pct_fail +FROM + `httparchive.all.pages` +WHERE + date = '2024-06-01' +GROUP BY + client, + is_root_page +ORDER BY + client, + is_root_page, + null_count, + pass_count, + fail_count diff --git a/sql/2024/page-weight/cwv_trend.sql b/sql/2024/page-weight/cwv_trend.sql new file mode 100644 index 00000000000..12383b28dc8 --- /dev/null +++ b/sql/2024/page-weight/cwv_trend.sql @@ -0,0 +1,86 @@ +WITH metrics_data AS ( + SELECT + date, + client, + CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64) AS bytes_total, + CAST(JSON_VALUE(lighthouse, '$.audits.largest-contentful-paint.numericValue') AS FLOAT64) AS lcp, + CAST(JSON_VALUE(lighthouse, '$.audits.cumulative-layout-shift.numericValue') AS FLOAT64) AS cls, + CAST(JSON_VALUE(lighthouse, '$.audits.total-blocking-time.numericValue') AS FLOAT64) AS tbt, + CAST(JSON_VALUE(lighthouse, '$.audits.first-contentful-paint.numericValue') AS FLOAT64) AS fcp, + CAST(JSON_VALUE(lighthouse, '$.audits.interactive.numericValue') AS FLOAT64) AS tti, + CAST(JSON_VALUE(lighthouse, '$.categories.performance.score') AS FLOAT64) AS performance_score + FROM + `httparchive.all.pages` + WHERE + date >= '2023-06-01' AND + date <= '2024-06-01' AND + EXTRACT(DAY FROM date) = 1 -- Only include data from the first day of each month +) +SELECT + date, + client, + COUNT(DISTINCT IF(bytes_total > 0, bytes_total, NULL)) AS total_pages, + + -- Page Size metrics + ROUND(APPROX_QUANTILES(bytes_total, 1000)[OFFSET(100)] / 1024, 2) AS p10_page_size_kb, + ROUND(APPROX_QUANTILES(bytes_total, 1000)[OFFSET(250)] / 1024, 2) AS p25_page_size_kb, + ROUND(APPROX_QUANTILES(bytes_total, 1000)[OFFSET(500)] / 1024, 2) AS p50_page_size_kb, + ROUND(APPROX_QUANTILES(bytes_total, 1000)[OFFSET(750)] / 1024, 2) AS p75_page_size_kb, + ROUND(APPROX_QUANTILES(bytes_total, 1000)[OFFSET(900)] / 1024, 2) AS p90_page_size_kb, + + -- LCP metrics + ROUND(APPROX_QUANTILES(lcp, 1000)[OFFSET(100)], 2) AS p10_lcp, + ROUND(APPROX_QUANTILES(lcp, 1000)[OFFSET(250)], 2) AS p25_lcp, + ROUND(APPROX_QUANTILES(lcp, 1000)[OFFSET(500)], 2) AS p50_lcp, + ROUND(APPROX_QUANTILES(lcp, 1000)[OFFSET(750)], 2) AS p75_lcp, + ROUND(APPROX_QUANTILES(lcp, 1000)[OFFSET(900)], 2) AS p90_lcp, + + -- CLS metrics + ROUND(APPROX_QUANTILES(cls, 1000)[OFFSET(100)], 3) AS p10_cls, + ROUND(APPROX_QUANTILES(cls, 1000)[OFFSET(250)], 3) AS p25_cls, + ROUND(APPROX_QUANTILES(cls, 1000)[OFFSET(500)], 3) AS p50_cls, + ROUND(APPROX_QUANTILES(cls, 1000)[OFFSET(750)], 3) AS p75_cls, + ROUND(APPROX_QUANTILES(cls, 1000)[OFFSET(900)], 3) AS p90_cls, + + -- TBT metrics (as a proxy for FID) + ROUND(APPROX_QUANTILES(tbt, 1000)[OFFSET(100)], 2) AS p10_tbt, + ROUND(APPROX_QUANTILES(tbt, 1000)[OFFSET(250)], 2) AS p25_tbt, + ROUND(APPROX_QUANTILES(tbt, 1000)[OFFSET(500)], 2) AS p50_tbt, + ROUND(APPROX_QUANTILES(tbt, 1000)[OFFSET(750)], 2) AS p75_tbt, + ROUND(APPROX_QUANTILES(tbt, 1000)[OFFSET(900)], 2) AS p90_tbt, + + -- FCP metrics + ROUND(APPROX_QUANTILES(fcp, 1000)[OFFSET(100)], 2) AS p10_fcp, + ROUND(APPROX_QUANTILES(fcp, 1000)[OFFSET(250)], 2) AS p25_fcp, + ROUND(APPROX_QUANTILES(fcp, 1000)[OFFSET(500)], 2) AS p50_fcp, + ROUND(APPROX_QUANTILES(fcp, 1000)[OFFSET(750)], 2) AS p75_fcp, + ROUND(APPROX_QUANTILES(fcp, 1000)[OFFSET(900)], 2) AS p90_fcp, + + -- TTI metrics + ROUND(APPROX_QUANTILES(tti, 1000)[OFFSET(100)], 2) AS p10_tti, + ROUND(APPROX_QUANTILES(tti, 1000)[OFFSET(250)], 2) AS p25_tti, + ROUND(APPROX_QUANTILES(tti, 1000)[OFFSET(500)], 2) AS p50_tti, + ROUND(APPROX_QUANTILES(tti, 1000)[OFFSET(750)], 2) AS p75_tti, + ROUND(APPROX_QUANTILES(tti, 1000)[OFFSET(900)], 2) AS p90_tti, + + -- Performance Score metrics + ROUND(APPROX_QUANTILES(performance_score, 1000)[OFFSET(900)] * 100, 2) AS p10_performance_score, + ROUND(APPROX_QUANTILES(performance_score, 1000)[OFFSET(750)] * 100, 2) AS p25_performance_score, + ROUND(APPROX_QUANTILES(performance_score, 1000)[OFFSET(500)] * 100, 2) AS p50_performance_score, + ROUND(APPROX_QUANTILES(performance_score, 1000)[OFFSET(250)] * 100, 2) AS p75_performance_score, + ROUND(APPROX_QUANTILES(performance_score, 1000)[OFFSET(100)] * 100, 2) AS p90_performance_score, + + -- Good CWV percentages + ROUND(COUNTIF(lcp <= 2500) / COUNT(0) * 100, 2) AS good_lcp_percent, + ROUND(COUNTIF(cls <= 0.1) / COUNT(0) * 100, 2) AS good_cls_percent, + ROUND(COUNTIF(tbt <= 300) / COUNT(0) * 100, 2) AS good_tbt_percent, + ROUND(COUNTIF(fcp <= 1800) / COUNT(0) * 100, 2) AS good_fcp_percent + +FROM + metrics_data +GROUP BY + date, + client +ORDER BY + date DESC, + client diff --git a/sql/2024/page-weight/facades-usage.sql b/sql/2024/page-weight/facades-usage.sql new file mode 100644 index 00000000000..3153db14ce5 --- /dev/null +++ b/sql/2024/page-weight/facades-usage.sql @@ -0,0 +1,22 @@ +SELECT + client, + is_root_page, + COUNTIF(JSON_VALUE(lighthouse, '$.audits.third-party-facades.score') IS NULL) AS null_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.third-party-facades.score') AS FLOAT64) >= 0.9) AS pass_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) AS fail_count, + COUNT(0) AS total, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.third-party-facades.score') AS FLOAT64) >= 0.9) / COUNT(0) AS pct_pass, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) / COUNT(0) AS pct_fail +FROM + `httparchive.all.pages` +WHERE + date = '2024-06-01' +GROUP BY + client, + is_root_page +ORDER BY + client, + is_root_page, + null_count, + pass_count, + fail_count diff --git a/sql/2024/page-weight/minified_css_usage.sql b/sql/2024/page-weight/minified_css_usage.sql new file mode 100644 index 00000000000..96b56c0614c --- /dev/null +++ b/sql/2024/page-weight/minified_css_usage.sql @@ -0,0 +1,22 @@ +SELECT + client, + is_root_page, + COUNTIF(JSON_VALUE(lighthouse, '$.audits.unminified-css.score') IS NULL) AS null_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-css.score') AS FLOAT64) >= 0.9) AS pass_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-css.score') AS FLOAT64) < 0.9) AS fail_count, + COUNT(0) AS total, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-css.score') AS FLOAT64) >= 0.9) / COUNT(0) AS pct_pass, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-css.score') AS FLOAT64) < 0.9) / COUNT(0) AS pct_fail +FROM + `httparchive.all.pages` +WHERE + date = '2024-06-01' +GROUP BY + client, + is_root_page +ORDER BY + client, + is_root_page, + null_count, + pass_count, + fail_count diff --git a/sql/2024/page-weight/minified_js_usage.sql b/sql/2024/page-weight/minified_js_usage.sql new file mode 100644 index 00000000000..b0d2fc33a1d --- /dev/null +++ b/sql/2024/page-weight/minified_js_usage.sql @@ -0,0 +1,22 @@ +SELECT + client, + is_root_page, + COUNTIF(JSON_VALUE(lighthouse, '$.audits.unminified-javascript.score') IS NULL) AS null_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-javascript.score') AS FLOAT64) >= 0.9) AS pass_count, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-javascript.score') AS FLOAT64) < 0.9) AS fail_count, + COUNT(0) AS total, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-javascript.score') AS FLOAT64) >= 0.9) / COUNT(0) AS pct_pass, + COUNTIF(SAFE_CAST(JSON_VALUE(lighthouse, '$.audits.unminified-javascript.score') AS FLOAT64) < 0.9) / COUNT(0) AS pct_fail +FROM + `httparchive.all.pages` +WHERE + date = '2024-06-01' +GROUP BY + client, + is_root_page +ORDER BY + client, + is_root_page, + null_count, + pass_count, + fail_count diff --git a/sql/2024/page-weight/page_weight_trend.sql b/sql/2024/page-weight/page_weight_trend.sql new file mode 100644 index 00000000000..d7abe282593 --- /dev/null +++ b/sql/2024/page-weight/page_weight_trend.sql @@ -0,0 +1,23 @@ +SELECT + date, + client, + is_root_page, + ROUND(APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64), 1000)[OFFSET(100)] / 1024, 2) AS p10, + ROUND(APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64), 1000)[OFFSET(250)] / 1024, 2) AS p25, + ROUND(APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64), 1000)[OFFSET(500)] / 1024, 2) AS p50, + ROUND(APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64), 1000)[OFFSET(750)] / 1024, 2) AS p75, + ROUND(APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64), 1000)[OFFSET(900)] / 1024, 2) AS p90 +FROM + `httparchive.all.pages` +WHERE + date >= '2023-06-01' AND date <= '2024-06-01' AND -- Adjust this range as needed + CAST(JSON_VALUE(summary, '$.bytesTotal') AS INT64) > 0 AND + EXTRACT(DAY FROM date) = 1 -- Only include data from the first day of each month +GROUP BY + date, + client, + is_root_page +ORDER BY + date DESC, + client, + is_root_page diff --git a/sql/2024/page-weight/request_type_distribution.sql b/sql/2024/page-weight/request_type_distribution.sql new file mode 100644 index 00000000000..6be041822a4 --- /dev/null +++ b/sql/2024/page-weight/request_type_distribution.sql @@ -0,0 +1,25 @@ +SELECT + percentile, + client, + is_root_page, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqTotal') AS INT64), 1000)[OFFSET(percentile * 10)] AS total_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqHtml') AS INT64), 1000)[OFFSET(percentile * 10)] AS html_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqJS') AS INT64), 1000)[OFFSET(percentile * 10)] AS js_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqCss') AS INT64), 1000)[OFFSET(percentile * 10)] AS css_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqImg') AS INT64), 1000)[OFFSET(percentile * 10)] AS img_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqJson') AS INT64), 1000)[OFFSET(percentile * 10)] AS json_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqOther') AS INT64), 1000)[OFFSET(percentile * 10)] AS other_req, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.reqFont') AS INT64), 1000)[OFFSET(percentile * 10)] AS font_req +FROM + `httparchive.all.pages`, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +WHERE + date = '2024-06-01' -- Adjust this date as needed +GROUP BY + percentile, + client, + is_root_page +ORDER BY + client, + is_root_page, + percentile diff --git a/sql/2024/page-weight/response_format_distribution.sql b/sql/2024/page-weight/response_format_distribution.sql new file mode 100644 index 00000000000..e7826f0158f --- /dev/null +++ b/sql/2024/page-weight/response_format_distribution.sql @@ -0,0 +1,21 @@ +SELECT + client, + percentile, + CAST(JSON_VALUE(summary, '$.type') AS STRING) AS format, + is_root_page, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.respBodySize') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS resp_size +FROM + `httparchive.all.requests`, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +WHERE + date = '2024-06-01' -- Adjust this date as needed +GROUP BY + client, + percentile, + format, + is_root_page +ORDER BY + format, + client, + is_root_page, + percentile diff --git a/sql/2024/page-weight/response_type_distribution.sql b/sql/2024/page-weight/response_type_distribution.sql new file mode 100644 index 00000000000..21fd85bf54d --- /dev/null +++ b/sql/2024/page-weight/response_type_distribution.sql @@ -0,0 +1,21 @@ +SELECT + client, + percentile, + CAST(JSON_VALUE(summary, '$.type') AS STRING) AS type, + is_root_page, + APPROX_QUANTILES(CAST(JSON_VALUE(summary, '$.respSize') AS INT64) / 1024, 1000)[OFFSET(percentile * 10)] AS resp_size +FROM + `httparchive.all.requests`, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +WHERE + date = '2024-06-01' -- Adjust this date as needed +GROUP BY + client, + percentile, + type, + is_root_page +ORDER BY + client, + type, + is_root_page, + percentile From 673a3ea545ae4851d2181288ce7077b19727cef4 Mon Sep 17 00:00:00 2001 From: Nurullah Demir <13475745+nrllh@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:00:59 +0100 Subject: [PATCH 2/3] Add BibTeX citation box (#1325) (#3718) * Add BibTeX citation box (#1325) Implemented a citation box in BibTeX format as per the discussion in issue #1325. Details: https://github.com/HTTPArchive/almanac.httparchive.org/issues/1325 * Update page.css Fixing fff issue from linter. * Linting errors * Add DOI * Formatting * Internationalisation --------- Co-authored-by: Mike Gifford Co-authored-by: Barry Pollard --- src/static/css/page.css | 13 +++++++++++ src/templates/base/base_chapter.html | 34 ++++++++++++++++++++++++++++ src/templates/en/base.html | 2 ++ src/templates/es/base.html | 2 ++ src/templates/fr/base.html | 2 ++ src/templates/hi/base.html | 2 ++ src/templates/it/base.html | 2 ++ src/templates/ja/base.html | 2 ++ src/templates/nl/base.html | 2 ++ src/templates/pt/base.html | 2 ++ src/templates/ru/base.html | 2 ++ src/templates/tr/base.html | 2 ++ src/templates/uk/base.html | 2 ++ src/templates/zh-CN/base.html | 2 ++ src/templates/zh-TW/base.html | 2 ++ 15 files changed, 73 insertions(+) diff --git a/src/static/css/page.css b/src/static/css/page.css index 324ec2c36ea..450bf2a949e 100644 --- a/src/static/css/page.css +++ b/src/static/css/page.css @@ -292,6 +292,7 @@ } .authors, +.citation-box h2, .authors h2, .chapter-links, .chapter-links h2, @@ -382,6 +383,18 @@ margin-right: 0.25rem; } +.citation-box pre { + padding: 16px; + border: 1px solid #e0e0e0; + border-radius: 4px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + font-family: monospace; + background-color: #f9f9f9; + overflow-x: auto; + white-space: pre-wrap; /* Allow wrapping of long lines */ + margin: 0; +} + #chapter-navigation { padding: 16px 36px 50px 36px; padding: 1rem 2.25rem 3.125rem 2.25rem; diff --git a/src/templates/base/base_chapter.html b/src/templates/base/base_chapter.html index 8fe567d6abf..d6f1a22bff3 100644 --- a/src/templates/base/base_chapter.html +++ b/src/templates/base/base_chapter.html @@ -336,6 +336,37 @@

{% endmacro %} +{% macro render_bibtex() %} +

+ {{ self.citation() }} +

+
+ BibTeX +
+@inbook{WebAlmanac.{{ year }}.{{ metadata.get('title').replace(' ', '') }},
+author = "{% for author in metadata.get('authors') -%}
+  {%- set authordata = config.contributors.get(author, None) -%}
+  {%- if authordata %}
+    {%- set full_name = authordata.name.split(' ') -%}
+    {{ full_name[-1] }}{{ self.comma() }}{{ full_name[0] }}{% if full_name|length > 2 %} {{ full_name[1:-1]|join(' ') }}{% endif %}{% if not loop.last %}{{ self.and() }}{% endif %}
+  {%- else %}
+    {%- set full_name = author.split(' ') -%}
+    {{ full_name[-1] }}{{ self.comma() }}{{ full_name[0] }}{% if full_name|length > 2 %} {{ full_name[1:-1]|join(' ') }}{% endif %}{% if not loop.last %}{{ self.and() }}{% endif %}
+  {%- endif %}
+{%- endfor %}",
+title = "{{ metadata.get('title') }}",
+booktitle = "{{ self.citation_journal_title() }}",
+chapter = {{ chapter_config.chapter_number }},
+publisher = "HTTP Archive",
+year = "{{ year }}",
+language = "{{ language }}",
+{%- if metadata.get('doi') %}
+doi = "{{ metadata.get('doi')}}",{%- endif %}
+url = "https://almanac.httparchive.org/en/{{ year }}/{{ metadata.get('chapter') }}"
+}
+
+{% endmacro %} + {% macro render_prevnext() %} {% if prev_chapter %} {% if chapter_lang_exists(lang, year, prev_chapter['slug']) %} @@ -442,6 +473,9 @@

{{ render_authors() }}
+
+ {{ render_bibtex() }} +