Repository: freelawproject/eyecite

Issue:
Standardize page for NY reporters
Currently the only reporters with `()[]` characters in their `page`s are `NY Slip Op` and `Misc. 3d`
However, each of those admit 2 variations that are not equal and must be standardized

The desired behavior is:

```
In [4]: get_citations("2024 NY Slip Op 51192(U),")[0].corrected_citation()
Out[4]: '2024 NY Slip Op 51192(U)'

In [3]: get_citations("2024 NY Slip Op 51192[U],")[0].corrected_citation()
Out[3]: '2024 NY Slip Op 51192(U)'

In [6]: get_citations("11 Misc 3d 134[A]")[0].corrected_citation()
Out[6]: '134(A)'

In [7]: get_citations("11 Misc 3d 134(A)")[0].corrected_citation()
Out[7]: '134(A)'
```

To do this, we can add a page correction method, to be called inside `corrected_citation()`

Discussion:

Solution patch:
diff --git a/CHANGES.md b/CHANGES.md
index 1ae8b80..70f2ccc 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,7 +8,7 @@ Features:
 - Adds helper function span_with_pincite() to get full citation with pin cite
 
 Changes:
-- None
+- Add page correction to `corrected_citation`, for 'NY Slip Op' and 'Misc 3d'
 
 Fixes:
 - Strengthens error handling during the loading of the cached Hyperscan database. This ensures that an invalid cache triggers a rebuild.
diff --git a/eyecite/models.py b/eyecite/models.py
index 3eb56df..0090d58 100644
--- a/eyecite/models.py
+++ b/eyecite/models.py
@@ -15,7 +15,7 @@ from typing import (
     cast,
 )
 
-from eyecite.utils import hash_sha256
+from eyecite.utils import REPORTERS_THAT_NEED_PAGE_CORRECTION, hash_sha256
 
 ResourceType = Hashable
 
@@ -308,12 +308,36 @@ class ResourceCitation(CitationBase):
         )
 
     def corrected_citation(self):
-        """Return citation with corrected reporter."""
+        """Return citation with corrected reporter and standardized page"""
+        corrected = self.matched_text()
         if self.edition_guess:
-            return self.matched_text().replace(
-                self.groups["reporter"], self.edition_guess.short_name
+            corrected = corrected.replace(
+                self.groups.get("reporter"), self.edition_guess.short_name
             )
-        return self.matched_text()
+
+        corrected_page = self.corrected_page()
+        if corrected_page and corrected_page != self.groups["page"]:
+            corrected = corrected.replace(self.groups["page"], corrected_page)
+
+        return corrected
+
+    def corrected_page(self):
+        """Can we standardize a page value?"""
+        page = self.groups.get("page")
+        if page is None:
+            return
+
+        standard_reporter = ""
+        if reporter := self.groups.get("reporter"):
+            if self.edition_guess:
+                standard_reporter = self.edition_guess.short_name
+            if {
+                reporter,
+                standard_reporter,
+            } & REPORTERS_THAT_NEED_PAGE_CORRECTION:
+                return page.replace("[U]", "(U)").replace("[A]", "(A)")
+
+        return page
 
     def guess_edition(self):
         """Set edition_guess."""
diff --git a/eyecite/utils.py b/eyecite/utils.py
index c8d1dc5..5c21d4b 100644
--- a/eyecite/utils.py
+++ b/eyecite/utils.py
@@ -4,6 +4,8 @@ import re
 
 from lxml import etree
 
+REPORTERS_THAT_NEED_PAGE_CORRECTION = {"NY Slip Op", "Misc. 3d"}
+
 # Names not allowed to be reference citations
 # this is partially taken from juriscraper
 DISALLOWED_NAMES = [
diff --git a/pyproject.toml b/pyproject.toml
index d1f8791..7b613e9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,7 +29,7 @@ include = ["eyecite/py.typed"]
 
 [tool.poetry.dependencies]
 python = ">=3.10,<4.0"
-reporters-db = ">=3.2.52"
+reporters-db = ">=3.2.53"
 lxml = ">=4.6.3"
 pyahocorasick = ">= 1.2"
 fast-diff-match-patch = ">=2.0.0"


Test patch:
diff --git a/tests/test_ModelsTest.py b/tests/test_ModelsTest.py
index cb59efb..6e8b261 100644
--- a/tests/test_ModelsTest.py
+++ b/tests/test_ModelsTest.py
@@ -221,3 +221,35 @@ class ModelsTest(TestCase):
             full_case_citation.corrected_citation_full(),
             "Bank v. Vinson, 477 U.S. 57, 60 (scotus 1986)",
         )
+
+    def test_page_correction(self):
+        """Can we correct pages on citation.corrected_citation()?"""
+        tests = [
+            (
+                "2024 N.Y. Slip Op. 51192(U)",
+                "2024 NY Slip Op 51192(U)",
+                "51192(U)",
+            ),
+            ("2024 NYSlipOp 51192[U]", "2024 NY Slip Op 51192(U)", "51192(U)"),
+            ("11 Misc 3d 134[A]", "11 Misc. 3d 134(A)", "134(A)"),
+            ("83 Misc.3d 126(A)", "83 Misc. 3d 126(A)", "126(A)"),
+            # cases where no page correction should happen
+            ("11 U.S. 11[2]", "11 U.S. 11", "11"),
+            (
+                "Tex. Civ. Prac. & Rem. Code Ann. § 171.023",
+                "Tex. Code Ann. § 171.023",
+                None,
+            ),
+        ]
+        for citation, corrected_citation, corrected_page in tests:
+            cite = get_citations(citation)[0]
+            self.assertEqual(
+                cite.corrected_citation(),
+                corrected_citation,
+                "Page correction not working",
+            )
+            self.assertEqual(
+                cite.corrected_page(),
+                corrected_page,
+                "Standalone page correction not working",
+            )