fix(verify_pdf): --ascii-dates catches the 2016–17 form again (regression in #533) (#698)

* fix(verify_pdf): --ascii-dates catches the 2016-17 form again

The quantity rule from #533 treated "17" as a bare non-year number, so
"MSc Physics, 2016-17", "Sep 2016-17" and "2019-20 season" passed /apply
Step 5d although the guide says a year on either side of the dash is
enough, and all three were caught before #533.

A year followed by a two-digit year is now a date pair; "1500-2000" and
"2000-5000" stay quantities, and "17-2016" (number first) is not a date
anyone writes. Two new cases; the date-pair one fails on master.

* changelog: place the #698 entry under [Unreleased]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-20 21:00:46 +02:00
1 parent 2b4097177a
commit 2aadb070f4
3 files changed
+32 -2

No files matched your search

+7
View File
@@ -301,6 +301,13 @@ per-file diff commands.
(detail unavailable, synthesised from search)`, documented in the CLI README, and covered (detail unavailable, synthesised from search)`, documented in the CLI README, and covered
by automated tests. by automated tests.
- **`verify_pdf.py --ascii-dates` catches the `2016–17` form again** (#698) - the quantity rule from
#533 treated `17` as a bare non-year number, so `MSc Physics, 2016–17`, `Sep 2016–17` and
`2019–20 season` passed `/apply` Step 5d although the guide says a year on either side of the
dash is enough, and all three were caught before #533. A year followed by a two-digit year is
now a date pair; `1500–2000` and `2000–5000` stay quantities. Two new `test_verify_pdf.py`
cases; the date-pair one fails on master.
### Security ### Security
- **CI no longer leaves the job token in `.git/config` or expands a PR-controlled name into a - **CI no longer leaves the job token in `.git/config` or expands a PR-controlled name into a
+11
View File
@@ -134,6 +134,17 @@ class FindNonAsciiDateRangesTests(unittest.TestCase):
with self.subTest(text=text): with self.subTest(text=text):
self.assertEqual(len(find_non_ascii_date_ranges(text)), 1) self.assertEqual(len(find_non_ascii_date_ranges(text)), 1)
def test_year_and_two_digit_year_is_a_date_range(self):
# The academic-year and season form, caught before the quantity rule
# (#533) and suppressed by it because `17` is a bare non-year number.
for text in ("MSc Physics, 2016\u201317", "Sep 2016\u201317", "2019\u201320 season", "2016 \u2013 17"):
with self.subTest(text=text):
self.assertEqual(len(find_non_ascii_date_ranges(text)), 1)
def test_two_digit_number_before_a_year_is_still_a_quantity(self):
# Only year-first is the date form; `17-2016` is not a date anyone writes.
self.assertEqual(find_non_ascii_date_ranges("items 17\u20132016"), [])
def test_two_years_joined_by_a_dash_are_still_a_date_range(self): def test_two_years_joined_by_a_dash_are_still_a_date_range(self):
for text in ("2016\u20132024", "1990\u20132010 programme", "Roles 2000\u20132024"): for text in ("2016\u20132024", "1990\u20132010 programme", "Roles 2000\u20132024"):
with self.subTest(text=text): with self.subTest(text=text):
+14 -2
View File
@@ -132,8 +132,18 @@ def _bare_number(token):
return core return core
_TWO_DIGIT_YEAR = re.compile(r"\d{2}")
def _quantity_range(line, dash_index): def _quantity_range(line, dash_index):
"""True when both sides of the dash are bare numbers and not both years.""" """True when both sides of the dash are bare numbers and not a date pair.
A date pair is two years (`2016-2024`) or a year and a two-digit year
(`2016-17`, `2019-20`): the short form is how academic years and seasons
are written, and it was caught before the quantity rule existed. Anything
else with bare numbers on both sides is a quantity (`1500-2000`,
`2000-5000`) and is left alone.
"""
before = _TOKEN_BEFORE.search(line[:dash_index]) before = _TOKEN_BEFORE.search(line[:dash_index])
after = _TOKEN_AFTER.search(line[dash_index + 1 :]) after = _TOKEN_AFTER.search(line[dash_index + 1 :])
if not before or not after: if not before or not after:
@@ -141,7 +151,9 @@ def _quantity_range(line, dash_index):
left, right = _bare_number(before.group(1)), _bare_number(after.group(1)) left, right = _bare_number(before.group(1)), _bare_number(after.group(1))
if left is None or right is None: if left is None or right is None:
return False return False
return not (_BARE_YEAR.fullmatch(left) and _BARE_YEAR.fullmatch(right)) if _BARE_YEAR.fullmatch(left) and (_BARE_YEAR.fullmatch(right) or _TWO_DIGIT_YEAR.fullmatch(right)):
return False
return True
def find_non_ascii_date_ranges(text): def find_non_ascii_date_ranges(text):