Skip to content

Citation Report

Concepts

Definition

There are three popular repositories of Philippine Supreme Court decisions. Reference to such decisions are (traditionally) based on the name of the repository. Two of which - Phil. and SCRA - are talked about in the case of China Airlines v. Chiok, G.R. No. 152122, July 30, 2003:

x x x This Court hereby exhorts members of the bar and the bench to refer to and quote from the official repository of our decisions, the Philippine Reports, whenever practicable [footnote: In the present case, Philippine Reports are cited whenever possible.]. In the absence of this primary source, which is still being updated, they may resort to unofficial sources like the SCRA [footnote: Supreme Court Reports Annotated]. We remind them that the Court’s ponencia, when used to support a judgment or ruling, should be quoted accurately. (emphasis and footnotes supplied)

Kinds

Style Nature Publisher Description Basis
Phil. Public Philippine Reports Though "official", often delayed in publication The [Supreme Court] Reporter shall prepare and publish with each reported decision a concise synopsis of the facts necessary to a clear understanding of the case x x x
Offg. Public Official Gazette Though "official", decisions are only published here occasionally There shall be published in the Official Gazette all x x x decisions or abstracts of decisions of the Supreme Court and the Court of Appeals, or other courts of similar rank, as may be deemed by the said courts of sufficient importance to be so published; x x x
SCRA Private Supreme Court Reports Annotated An unofficial source but more frequently printed by private entity See disquisition in China Airlines v. Chiok (2003)

Formats

Each of the Report citations above have the same format... differing only in the publisher involved. Because of inconsistent styling over the years -- e.g. instead of Phil., what will be written is Phil Rep. -- it's necessary to create a uniform format using a volume publisher page format:

  Volume Publisher Page/s Date
Sample Inconsistencies Volume no. Name / style of the publisher / reporter Page number/s of the volume Optional date
1 Phil. Reports 100 is equivalent to 1 Phil. 100 1 Phil. 100 -
100 S.C.R.A. 105, 101-103 (1994) is equivalent to 100 SCRA 105 1 SCRA 105 -
41 Off. Gazette 1001, Jan. 1, 1949 is equivalent to 41 O.G. 1001 41 O.G. 1001 Jan. 1, 1949

API

Report Model

Bases: BaseModel

The REPORT_PATTERN is a re.Pattern object that contains pre-defined regex group names. These group names can be mapped to the Report model's fields:

Field Type Description
publisher optional (str) Type of the publisher.
volume optional (str) Publisher volume number.
page optional (str) Publisher volume page.
volpubpage optional (str) Combined fields:
report_date optional (date) Optional date associated with the report citation

It's important that each field be optional. The Report will be joined to another BaseModel object, i.e. the Docket, in a third-party library. It must be stressed that the Report object is only one part of the eventual DockerReportCitation object. It can:

  1. have both a Docket and a Report,
  2. have just a Docket;
  3. have just a Report.

If the value of the property exists, it represents whole volpubpage value.

  1. @phil
  2. @scra
  3. @offg
Source code in citation_report/__main__.py
Python
class Report(BaseModel):
    """The [REPORT_PATTERN][report-pattern] is a `re.Pattern` object that
    contains pre-defined regex group names. These group names can be mapped
    to the `Report` model's fields:

    Field | Type | Description
    --:|:--:|:--
    `publisher` | optional (str) | Type of the publisher.
    `volume` | optional (str) | Publisher volume number.
    `page` | optional (str) | Publisher volume page.
    `volpubpage` | optional (str) | Combined fields: <volume> <publisher> <page>
    `report_date` | optional (date) | Optional date associated with the report citation

    It's important that each field be **optional**. The `Report` will be joined
    to another `BaseModel` object, i.e. the `Docket`, in a third-party library.
    It must be stressed that the `Report` object is only one part of
    the eventual `DockerReportCitation` object. It can:

    1. have both a `Docket` and a `Report`,
    2. have just a `Docket`;
    3. have just a `Report`.

    If the value of the property exists, it represents whole `volpubpage` value.

    1. `@phil`
    2. `@scra`
    3. `@offg`
    """

    publisher: str | None = Field(
        None,
        title="Report Publisher",
        description=(
            "Shorthand label of the publisher involved, e.g. SCRA, Phil. Offg"
        ),
        max_length=5,
    )
    volume: str | None = Field(
        None,
        title="Volume Number",
        description=(
            "Publisher volume number - may not be a full digit since it can"
            " exceptionally include letters."
        ),
        max_length=10,
    )
    page: str | None = Field(
        None,
        title="Page Number",
        description="The page number of the publisher volume involved",
        max_length=5,
    )
    volpubpage: str | None = Field(
        None,
        title="Volume Publisher Page",
        description="Full expression of the report citation",
        max_length=20,
    )
    report_date: datetime.date | None = Field(
        None,
        title="Report Date",
        description="Exceptionally, report citations reference dates.",
    )

    def __str__(self) -> str:
        return self.volpubpage or ""

    @property
    def phil(self):
        return self.volpubpage if self.publisher == PHIL.label else None

    @property
    def scra(self):
        return self.volpubpage if self.publisher == SCRA.label else None

    @property
    def offg(self):
        return self.volpubpage if self.publisher == OFFG.label else None

    @classmethod
    def extract_report(cls, text: str) -> Iterator[Self]:
        """Given sample legalese `text`, extract all Supreme Court `Report` patterns.

        Examples:
            >>> sample = "250 Phil. 271, 271-272, Jan. 1, 2019"
            >>> report = next(Report.extract(sample))
            >>> type(report)
            citation_report.__main__.Report
            >>> report.volpubpage
            '250 Phil. 271'

        Args:
            text (str): Text containing report citations.

        Yields:
            Iterator[Self]: Iterator of `Report` instances
        """
        for match in REPORT_PATTERN.finditer(text):
            report_date = None
            if text := match.group("report_date"):
                try:
                    report_date = parse(text).date()
                except Exception:
                    report_date = None

            publisher = get_publisher_label(match)
            volume = match.group("volume")
            page = match.group("page")

            if publisher and volume and page:
                yield Report(
                    publisher=publisher,
                    volume=volume,
                    page=page,
                    volpubpage=match.group("volpubpage"),
                    report_date=report_date,
                )

    @classmethod
    def extract_from_dict(cls, data: dict, report_type: str) -> str | None:
        """Assuming a dictionary with any of the following report_type keys
        `scra`, `phil` or `offg`, get the value of the Report property.

        Examples:
            >>> sample_data = {"scra": "14 SCRA 314"} # dict
            >>> Report.extract_from_dict(sample_data, "scra")
            '14 SCRA 314'

        Args:
            data (dict): A `dict` containing a possible report `{key: value}`
            report_type (str): Must be either "scra", "phil", or "offg"

        Returns:
            str | None: The value of the key `report_type` in the `data` dict.
        """
        if report_type.lower() in ["scra", "phil", "offg"]:
            if candidate := data.get(report_type):
                try:
                    obj = next(cls.extract_report(candidate))
                    # will get the @property of the Report with the same name
                    if hasattr(obj, report_type):
                        return obj.__getattribute__(report_type)
                except StopIteration:
                    return None
        return None

    @classmethod
    def get_unique(cls, text: str) -> list[str]:
        """Will only get `Report` volpubpages (string) from the text. This
        is used later in `citation_utils` to prevent duplicate citations.

        Examples:
            >>> text = "(22 Phil. 303; 22 Phil. 303; 176 SCRA 240; Peñalosa v. Tuason, 22 Phil. 303, 313 (1912); Heirs of Roxas v. Galido, 108 Phil. 582 (1960)); Valmonte v. PCSO, supra; Bugnay Const. and Dev. Corp. v. Laron, 176 SCRA 240 (1989)"
            >>> Report.get_unique(text)
            ['22 Phil. 303', '108 Phil. 582', '176 SCRA 240']

        Args:
            text (str): Text to search for report patterns

        Returns:
            list[str]: Unique report `volpubpage` strings found in the text
        """  # noqa: E501
        return list(
            {r.volpubpage for r in cls.extract_report(text) if r.volpubpage}
        )

Functions

extract_from_dict(data, report_type) classmethod

Assuming a dictionary with any of the following report_type keys scra, phil or offg, get the value of the Report property.

Examples:

Python Console Session
>>> sample_data = {"scra": "14 SCRA 314"} # dict
>>> Report.extract_from_dict(sample_data, "scra")
'14 SCRA 314'

Parameters:

Name Type Description Default
data dict

A dict containing a possible report {key: value}

required
report_type str

Must be either "scra", "phil", or "offg"

required

Returns:

Type Description
str | None

str | None: The value of the key report_type in the data dict.

Source code in citation_report/__main__.py
Python
@classmethod
def extract_from_dict(cls, data: dict, report_type: str) -> str | None:
    """Assuming a dictionary with any of the following report_type keys
    `scra`, `phil` or `offg`, get the value of the Report property.

    Examples:
        >>> sample_data = {"scra": "14 SCRA 314"} # dict
        >>> Report.extract_from_dict(sample_data, "scra")
        '14 SCRA 314'

    Args:
        data (dict): A `dict` containing a possible report `{key: value}`
        report_type (str): Must be either "scra", "phil", or "offg"

    Returns:
        str | None: The value of the key `report_type` in the `data` dict.
    """
    if report_type.lower() in ["scra", "phil", "offg"]:
        if candidate := data.get(report_type):
            try:
                obj = next(cls.extract_report(candidate))
                # will get the @property of the Report with the same name
                if hasattr(obj, report_type):
                    return obj.__getattribute__(report_type)
            except StopIteration:
                return None
    return None

extract_report(text) classmethod

Given sample legalese text, extract all Supreme Court Report patterns.

Examples:

Python Console Session
>>> sample = "250 Phil. 271, 271-272, Jan. 1, 2019"
>>> report = next(Report.extract(sample))
>>> type(report)
citation_report.__main__.Report
>>> report.volpubpage
'250 Phil. 271'

Parameters:

Name Type Description Default
text str

Text containing report citations.

required

Yields:

Type Description
Iterator[Self]

Iterator[Self]: Iterator of Report instances

Source code in citation_report/__main__.py
Python
@classmethod
def extract_report(cls, text: str) -> Iterator[Self]:
    """Given sample legalese `text`, extract all Supreme Court `Report` patterns.

    Examples:
        >>> sample = "250 Phil. 271, 271-272, Jan. 1, 2019"
        >>> report = next(Report.extract(sample))
        >>> type(report)
        citation_report.__main__.Report
        >>> report.volpubpage
        '250 Phil. 271'

    Args:
        text (str): Text containing report citations.

    Yields:
        Iterator[Self]: Iterator of `Report` instances
    """
    for match in REPORT_PATTERN.finditer(text):
        report_date = None
        if text := match.group("report_date"):
            try:
                report_date = parse(text).date()
            except Exception:
                report_date = None

        publisher = get_publisher_label(match)
        volume = match.group("volume")
        page = match.group("page")

        if publisher and volume and page:
            yield Report(
                publisher=publisher,
                volume=volume,
                page=page,
                volpubpage=match.group("volpubpage"),
                report_date=report_date,
            )

get_unique(text) classmethod

Will only get Report volpubpages (string) from the text. This is used later in citation_utils to prevent duplicate citations.

Examples:

Python Console Session
>>> text = "(22 Phil. 303; 22 Phil. 303; 176 SCRA 240; Peñalosa v. Tuason, 22 Phil. 303, 313 (1912); Heirs of Roxas v. Galido, 108 Phil. 582 (1960)); Valmonte v. PCSO, supra; Bugnay Const. and Dev. Corp. v. Laron, 176 SCRA 240 (1989)"
>>> Report.get_unique(text)
['22 Phil. 303', '108 Phil. 582', '176 SCRA 240']

Parameters:

Name Type Description Default
text str

Text to search for report patterns

required

Returns:

Type Description
list[str]

list[str]: Unique report volpubpage strings found in the text

Source code in citation_report/__main__.py
Python
@classmethod
def get_unique(cls, text: str) -> list[str]:
    """Will only get `Report` volpubpages (string) from the text. This
    is used later in `citation_utils` to prevent duplicate citations.

    Examples:
        >>> text = "(22 Phil. 303; 22 Phil. 303; 176 SCRA 240; Peñalosa v. Tuason, 22 Phil. 303, 313 (1912); Heirs of Roxas v. Galido, 108 Phil. 582 (1960)); Valmonte v. PCSO, supra; Bugnay Const. and Dev. Corp. v. Laron, 176 SCRA 240 (1989)"
        >>> Report.get_unique(text)
        ['22 Phil. 303', '108 Phil. 582', '176 SCRA 240']

    Args:
        text (str): Text to search for report patterns

    Returns:
        list[str]: Unique report `volpubpage` strings found in the text
    """  # noqa: E501
    return list(
        {r.volpubpage for r in cls.extract_report(text) if r.volpubpage}
    )

Report Pattern

A compiled regex expression that enables capturing the parts of a report.

Examples:

Python Console Session
>>> from citation_report import REPORT_PATTERN
>>> text = "42 SCRA 109, 117-118, October 29, 1971;"
>>> sample_match = REPORT_PATTERN.search(text)
>>> sample_match.group("volpubpage")
'42 SCRA 109'
>>> sample_match.group("volume")
'42 SCRA 109'
>>> sample_match.group("publisher")
'SCRA'
>>> sample_match.group("page")
'109'
>>> sample_match.group("REPORT_DATE_REGEX")
'October 29, 1971'

get_publisher_label()

Given a regex match object from Report Pattern, determine if it contains a group name representing a Report publisher.

Examples:

Python Console Session
>>> from citation_report import REPORT_PATTERN, get_publisher_label
>>> assert REPORT_PATTERN.search("124Phil.1241 statement") is None
>>> sample = "This is an example 124 Phil. 1241 statement"
>>> m = REPORT_PATTERN.search(sample)
>>> m
<re.Match object; span=(19, 33), match='124 Phil. 1241'>
>>> label = get_publisher_label(m)
>>> label
'Phil.'

Parameters:

Name Type Description Default
match Match

Based on a prior re.search or re.finditer result on text

required

Returns:

Type Description
str | None

str | None: The first matching publisher found

Source code in citation_report/publisher.py
Python
def get_publisher_label(match: Match) -> str | None:
    """Given a regex match object from [Report Pattern][report-pattern],
    determine if it contains a group name representing a Report publisher.

    Examples:
        >>> from citation_report import REPORT_PATTERN, get_publisher_label
        >>> assert REPORT_PATTERN.search("124Phil.1241 statement") is None
        >>> sample = "This is an example 124 Phil. 1241 statement"
        >>> m = REPORT_PATTERN.search(sample)
        >>> m
        <re.Match object; span=(19, 33), match='124 Phil. 1241'>
        >>> label = get_publisher_label(m)
        >>> label
        'Phil.'

    Args:
        match (Match): Based on a prior `re.search` or `re.finditer`
            result on text

    Returns:
        str | None: The first matching publisher found
    """
    for src in [PHIL, SCRA, OFFG]:
        if match.group(src.group_name):
            return src.label