diff --git a/_layouts/bib.liquid b/_layouts/bib.liquid index fda7c6a6..815c2b62 100644 --- a/_layouts/bib.liquid +++ b/_layouts/bib.liquid @@ -160,44 +160,71 @@ {% endif %} - + {% assign proceedings = 'inproceedings,incollection' | split: ',' %} {% assign thesis = 'thesis,mastersthesis,phdthesis' | split: ',' %} - {% if entry.type == 'article' %} - {% capture entrytype %}{{ entry.journal }}{% endcapture %} - {% elsif proceedings contains entry.type %} - {% capture entrytype %}In {{ entry.booktitle }}{% endcapture %} - {% elsif thesis contains entry.type %} - {% capture entrytype %}{{ entry.school }}{% endcapture %} - {% else %} - {% capture entrytype %}{% endcapture %} - {% endif %} - {% if entry.month %} - {% capture entrymonth %}{{ " " }}{{ entry.month | capitalize }}{% endcapture %} - {% endif %} - {% if entry.year %} - {% capture entryyear %}{{ " " }}{{ entry.year }}{% endcapture %} - {% endif %} - {% if entry.location %} - {% capture entrytype %}{{ entrytype }}{{ ", " }}{{ entry.location }}{% endcapture %} - {% endif -%} - {% if entry.additional_info %} - {% capture entrytype %}{{ entrytype }}{{ entry.additional_info | markdownify | remove: '

' | remove: '

' }}{% endcapture %} + {% assign abbr_text = entry.abbr | default: '' | strip %} + {% assign location_text = entry.location | default: '' | strip %} + {% assign status_text = entry.status | default: '' | strip %} + {% assign additional_info_text = entry.additional_info | default: '' | strip %} + {% assign note_text = entry.note | default: '' | strip %} + {% assign award_text = entry.award | default: '' | strip %} + {% assign award_name_downcase = entry.award_name | default: '' | downcase %} + {% if award_text != '' and award_name_downcase contains 'oral' %} + {% assign status_text = '' %} {% endif %} + {% assign abbreviated_months = 'jan,feb,mar,apr,aug,sep,sept,oct,nov,dec' | split: ',' %} + {% assign month_text = entry.month | default: '' | strip %} + {% assign month_downcase = month_text | downcase %} + {% capture publication_date -%} + {%- if month_text != '' -%}{{ month_text | capitalize }}{%- if abbreviated_months contains month_downcase -%}.{%- endif %} {% endif -%}{{ entry.year }} + {%- endcapture %} + {% assign publication_date_text = publication_date | strip %} + {% capture periodical -%} + {%- if abbr_text == '' -%} + {%- if entry.type == 'article' and entry.journal != blank -%}{{ entry.journal }}{%- assign has_full_venue = true -%} + {%- elsif proceedings contains entry.type and entry.booktitle != blank -%}In {{ entry.booktitle }}{%- assign has_full_venue = true -%} + {%- elsif thesis contains entry.type and entry.school != blank -%}{{ entry.school }}{%- assign has_full_venue = true -%} + {%- endif -%} + {%- endif -%} + {%- if has_full_venue and publication_date_text != '' -%}, {% endif -%}{{ publication_date_text }} + {%- if location_text != '' -%}{%- if has_full_venue or publication_date_text != '' -%}, {% endif -%}{{ entry.location }}{%- endif -%} + {%- if status_text != '' -%}{%- if has_full_venue or publication_date_text != '' or location_text != '' -%}, {% endif -%}{{ entry.status }}{%- endif -%} + {%- if additional_info_text != '' -%} + {%- assign additional_info_first_character = additional_info_text | slice: 0, 1 -%} + {%- unless additional_info_first_character == '.' or additional_info_first_character == '!' or additional_info_first_character == '?' or additional_info_first_character == ',' or additional_info_first_character == ';' or additional_info_first_character == ':' -%} + {%- if has_full_venue or publication_date_text != '' or location_text != '' or status_text != '' -%}. {% endif -%} + {%- endunless -%} + {{- entry.additional_info | markdownify | remove: '

' | remove: '

' -}} + {%- endif -%} + {%- endcapture %} + {% assign periodical_text = periodical | strip_html | strip %} + {% assign periodical_last_character = periodical_text | slice: -1, 1 %} + {% assign note_last_character = note_text | slice: -1, 1 %} + {% capture hook_exists %}{% file_exists _includes/hook/bib.liquid %}{% endcapture %} {% if hook_exists == 'true' %} {% include hook/bib.liquid %} {% endif %} - {% assign entrytype_text = entrytype | strip_html | strip %} - {% capture periodical %}{{ entrytype }}{% if entrytype_text != "" and entryyear != "" %}, {% endif %}{{ entrymonth }}{{ entryyear }}{% endcapture %} +
- {% if entry.abbr %} + {% if abbr_text != '' %} {{ entry.abbr }} {% endif %} - {{ periodical | strip }} + + {{- periodical | strip -}} + {%- unless periodical_text == '' + or periodical_last_character == '.' + or periodical_last_character == '!' + or periodical_last_character == '?' + -%} + . + {%- endunless -%} +
- {{ entry.note | strip }} + {{ entry.note | strip -}} + {%- unless note_text == '' or note_last_character == '.' or note_last_character == '!' or note_last_character == '?' %}.{% endunless %}
diff --git a/test/publication_metadata_test.rb b/test/publication_metadata_test.rb new file mode 100644 index 00000000..da972e5c --- /dev/null +++ b/test/publication_metadata_test.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'digest' +require 'minitest/autorun' +require 'liquid' + +class FileExistsTag < Liquid::Tag + def render(_context) + 'false' + end +end +Liquid::Template.register_tag('file_exists', FileExistsTag) + +class PublicationMetadataTest < Minitest::Test + TEMPLATE_PATH = File.expand_path('../_layouts/bib.liquid', __dir__) + TEMPLATE_SOURCE = File.read(TEMPLATE_PATH) + METADATA_SOURCE = TEMPLATE_SOURCE[/ .*?(?= )/m] + BUTTON_SOURCE = TEMPLATE_SOURCE[/ .*?(?= \{% if site\.enable_publication_badges %\})/m] + MASTER_BUTTON_SHA256 = 'e066afb79731deff79de68a946081564c007cdccde8642749a8f2915909d6edd' + + def render_metadata(entry) + Liquid::Template.parse(METADATA_SOURCE, error_mode: :strict).render!('entry' => entry) + end + + def visible_text(entry) + render_metadata(entry).gsub(/<[^>]*>/, ' ').gsub(/\s+/, ' ').strip + end + + def test_nonblank_abbr_unconditionally_suppresses_full_venue + article = visible_text('type' => 'article', 'abbr' => 'OOPSLA', 'journal' => 'Proc. ACM Program. Lang.', 'year' => '2025') + proceedings = visible_text('type' => 'inproceedings', 'abbr' => 'WWW', 'booktitle' => 'ACM Web Conference', 'year' => '2024') + + assert_equal 'OOPSLA 2025.', article + assert_equal 'WWW 2024.', proceedings + end + + def test_year_ended_line_has_one_terminal_period_and_preserves_month_style + assert_equal 'OOPSLA Oct. 2025.', visible_text('type' => 'article', 'abbr' => 'OOPSLA', 'month' => 'oct', 'year' => '2025') + assert_equal 'Test July 2025.', visible_text('type' => 'misc', 'abbr' => 'Test', 'month' => 'July', 'year' => '2025') + end + + def test_non_year_metadata_lines_also_end_with_one_period + location = visible_text('type' => 'inproceedings', 'abbr' => 'WWW', 'year' => '2024', 'location' => 'Singapore') + additional_info = visible_text( + 'type' => 'article', 'abbr' => 'OOPSLA', 'month' => 'oct', 'year' => '2025', + 'additional_info' => ' [Site](https://example.com), the ArXiv version' + ) + note = visible_text('type' => 'misc', 'abbr' => 'ArXiv', 'year' => '2025', 'note' => 'Useful note') + + assert_equal 'WWW 2024, Singapore.', location + assert_equal 'OOPSLA Oct. 2025. [Site](https://example.com), the ArXiv version.', additional_info + assert_equal 'ArXiv 2025. Useful note.', note + end + + def test_prepunctuated_metadata_is_not_double_punctuated + { '.' => 'Final.', '!' => 'Final!', '?' => 'Final?' }.each do |punctuation, info| + text = visible_text('type' => 'misc', 'abbr' => 'Test', 'year' => '2025', 'additional_info' => ". #{info}") + + assert text.end_with?(punctuation), text + refute_match(/[.!?]{2}\z/, text) + end + end + + def test_oral_rule_and_nonvenue_metadata_are_preserved + text = visible_text( + 'type' => 'inproceedings', 'abbr' => 'ICLR', 'year' => '2026', + 'award' => "ICLR'26 oral paper.", 'award_name' => 'ORAL', + 'status' => 'Selected for oral presentation', 'additional_info' => '. Oral paper (literal).' + ) + + refute_includes text, 'Selected for oral presentation' + assert_includes text, 'Oral paper (literal).' + end + + def test_publication_buttons_are_byte_for_byte_unchanged_from_master + assert_equal MASTER_BUTTON_SHA256, Digest::SHA256.hexdigest(BUTTON_SOURCE) + %w[Abs arXiv Bib PAPER PDF Supp Video Blog Code Poster Slides Website].each { |label| assert_includes BUTTON_SOURCE, ">#{label}<" } + assert_includes BUTTON_SOURCE, '{%- if entry.award_name %}{{ entry.award_name }}{% else %}Awarded{% endif -%}' + end +end