Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions bibtex_linter/ieeetran_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,37 @@
@linter_rule(entry_type=None)
def check_url_field(entry: BibTeXEntry) -> List[str]:
"""
Check that the `url` field is not set.
Additionally, if the `note` field is set, check that it conforms to the following schema:
Check that the `url` and `doi` fields are not set, since `IEEEtran.bst` does not render them properly.
Additionally, if the `note` field is set, check that it conforms to one of the following schemas:

```
[ONLINE]. Available: \\url{...}, Accessed: YYYY-mmm-dd
[ONLINE]. Available: \\url{...}, Accessed: YYYY-MM-DD
doi: \\href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}
```
Note, that the backslash had to be escaped here and is only meant to be a single one.
In the DOI form, the shown DOI must be the same as the DOI in the URL. Since a DOI is persistent, it has no
access date.

:param entry: The BibTeXEntry
:return: A list of string descriptions of rule violations for this entry.
"""
invariant_violations: List[str] = []
if "url" in entry.fields.keys():
invariant_violations.append(
"Contains the non-allowed field: [url]. "
"Move the content of the field into the [note] field."
)
for field in ("url", "doi"):
if field in entry.fields.keys():
invariant_violations.append(
f"Contains the non-allowed field: [{field}]. "
"Move the content of the field into the [note] field."
)
if "note" in entry.fields.keys():
note_content: str = entry.fields["note"]
pattern = r"^\[ONLINE\]\. Available: \\url\{(.+?)\}, Accessed: (\d{4}-\d{2}-\d{2})$"
match = re.match(pattern, note_content)
if not match:
online_pattern = r"^\[ONLINE\]\. Available: \\url\{(.+?)\}, Accessed: (\d{4}-\d{2}-\d{2})$"
doi_pattern = r"^doi: \\href\{https://doi\.org/(10\.\d{4,9}/\S+)\}\{\1\}$"
if not (re.match(online_pattern, note_content) or re.match(doi_pattern, note_content)):
invariant_violations.append(
"Contains a malformed field [note]. "
"Make sure the [note] field follows the following pattern: '[ONLINE]. Available: \\url{...}, "
"Accessed: YYYY-mmm-dd'"
"Make sure the [note] field follows one of the following patterns: "
"'[ONLINE]. Available: \\url{...}, Accessed: YYYY-MM-DD' or "
"'doi: \\href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}'"
)
return invariant_violations

Expand Down
64 changes: 48 additions & 16 deletions docs/IEEEtran_best_practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule below
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)

## Conference
- Entry names: `@conference`, `@inproceedings`
Expand All @@ -58,9 +62,13 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- Disallowed fields:
- `paper`: Seems to be not standard, so avoid using it
- `url`: See special rule below
- `doi`: See special rule below
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)
- If both `organization` and `publisher` have the exact same value, only use `publisher`.

## Online
Expand All @@ -78,9 +86,13 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule below
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)
- If `organization` and `author` are the same, only use `author`

## Book
Expand All @@ -102,9 +114,13 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule below
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)
- If `editor` and `publisher` are the same, only use `publisher`.

## InBook
Expand All @@ -128,10 +144,14 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule below
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- `editor`: Field is not rendered
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)

## InCollection
- Entry name: `@incollection`
Expand All @@ -153,10 +173,14 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule below
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- `type`: Disallowed, since if it was set to (Article, Paper, etc.), we should use the proper entry type instead.)
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)
- If `editor` and `publisher` are the same, use `publisher`.

## Standard
Expand All @@ -176,9 +200,13 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule bewlo
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)
- If `author` and `organization` are the same, use `organization`

> [!note]
Expand All @@ -200,9 +228,13 @@ This document is meant as a cheatsheet for how to set up your `refs.bib` file wh
- `note`: See special rule below
- Disallowed fields:
- `url`: See special rule below
- `doi`: See special rule below
- Special rules:
- `note` and `url` convention: Do not use `url`, rather format the `note` in the following pattern:
`[ONLINE]. Available: \url{...}, Accessed: YYYY-mmm-dd`
- `note`, `url` and `doi` convention: Do not use `url` or `doi`, rather format the `note` in one of the following
patterns:
- For links: `[ONLINE]. Available: \url{...}, Accessed: YYYY-MM-DD`
- For DOIs: `doi: \href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}` (no access date, the shown DOI must be the
same as the DOI in the URL)

> [!note]
> Try using the other entry types instead of this one, if possible.
Expand Down
61 changes: 59 additions & 2 deletions test/test_verification.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import unittest
from unittest import mock
from typing import List, Set

from bibtex_linter import verification
from bibtex_linter.verification import check_required_fields, check_omitted_fields, verify, linter_rule
from bibtex_linter.parser import BibTeXEntry
from bibtex_linter.ieeetran_rules import check_url_field


@linter_rule(entry_type="test_entry_type")
Expand Down Expand Up @@ -66,7 +69,9 @@ def test_verify_combined_rule(self) -> None:
"Has fields present that would be omitted in the compiled document: [url]. "
"This could lead to a loss of information."
]
actual = verify(entry)
# Only check the rule defined here, not the ones registered by importing a ruleset
with mock.patch.object(verification, "_rules", [example_linter_rule]):
actual = verify(entry)
self.assertEqual(expected, actual)

def test_verify_skips_different_entry_type(self) -> None:
Expand All @@ -75,10 +80,62 @@ def test_verify_skips_different_entry_type(self) -> None:
name="skipped_entry",
fields={"author": "Someone", "url": "http://example.org"}
)
actual = verify(entry)
with mock.patch.object(verification, "_rules", [example_linter_rule]):
actual = verify(entry)
expected: List[str] = [] # No rules should apply
self.assertEqual(expected, actual)


class TestIEEEtranUrlField(unittest.TestCase):
MALFORMED_NOTE = (
"Contains a malformed field [note]. "
"Make sure the [note] field follows one of the following patterns: "
"'[ONLINE]. Available: \\url{...}, Accessed: YYYY-MM-DD' or "
"'doi: \\href{https://doi.org/10.xxxx/yyy}{10.xxxx/yyy}'"
)

def test_online_note_valid(self) -> None:
entry = BibTeXEntry(
entry_type="misc",
name="online_note",
fields={"note": "[ONLINE]. Available: \\url{https://example.com}, Accessed: 2025-01-01"}
)
self.assertEqual([], check_url_field(entry))

def test_doi_note_valid(self) -> None:
entry = BibTeXEntry(
entry_type="article",
name="doi_note",
fields={"note": "doi: \\href{https://doi.org/10.1109/TPAMI.2008.12}{10.1109/TPAMI.2008.12}"}
)
self.assertEqual([], check_url_field(entry))

def test_doi_note_mismatching_doi(self) -> None:
entry = BibTeXEntry(
entry_type="article",
name="doi_mismatch",
fields={"note": "doi: \\href{https://doi.org/10.1109/TPAMI.2008.12}{10.1109/TPAMI.2008.13}"}
)
self.assertEqual([self.MALFORMED_NOTE], check_url_field(entry))

def test_doi_note_with_access_date(self) -> None:
entry = BibTeXEntry(
entry_type="article",
name="doi_accessed",
fields={"note": "doi: \\href{https://doi.org/10.1109/TPAMI.2008.12}{10.1109/TPAMI.2008.12}, "
"Accessed: 2025-01-01"}
)
self.assertEqual([self.MALFORMED_NOTE], check_url_field(entry))

def test_doi_field_disallowed(self) -> None:
entry = BibTeXEntry(
entry_type="article",
name="doi_field",
fields={"doi": "10.1109/TPAMI.2008.12"}
)
expected = ["Contains the non-allowed field: [doi]. Move the content of the field into the [note] field."]
self.assertEqual(expected, check_url_field(entry))


if __name__ == "__main__":
unittest.main()
Loading