Skip to content

Commit

Permalink
move add_opinion to Decision model
Browse files Browse the repository at this point in the history
  • Loading branch information
mscarey committed Aug 5, 2021
1 parent 2e810e1 commit 32aef31
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 60 deletions.
16 changes: 5 additions & 11 deletions authorityspoke/decisions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Optional, Sequence, Tuple, Union

from anchorpoint.textselectors import TextQuoteSelector, TextPositionSelector
from justopinion.decisions import CAPDecision as Decision
from justopinion.decisions import Decision

from justopinion.decisions import (
CAPCitation,
Expand Down Expand Up @@ -72,16 +72,10 @@ def find_matching_opinion(
opinion_type: str = "",
opinion_author: str = "",
) -> Optional[Opinion]:
if not opinion_type and not opinion_author:
if len(self.decision.opinions) == 1:
return self.decision.opinions[0]
return None
for opinion in self.decision.opinions:
if ((opinion_type == opinion.type) or not opinion_type) and (
(opinion_author == opinion.author) or not opinion_author
):
return opinion
return None
"""Find an Opinion described by the given attributes."""
return self.decision.find_matching_opinion(
opinion_type=opinion_type, opinion_author=opinion_author
)

def find_opinion_matching_reading(
self,
Expand Down
4 changes: 2 additions & 2 deletions authorityspoke/io/writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

from typing import Dict, List, Optional, Union

from justopinion.decisions import CAPDecision
from justopinion.decisions import Decision

from authorityspoke.decisions import Decision
from authorityspoke.io import filepaths


def case_to_file(
case: Union[CAPDecision, Decision],
case: Union[Decision, Decision],
filename: Optional[str] = None,
directory: Optional[pathlib.Path] = None,
filepath: Optional[pathlib.Path] = None,
Expand Down
2 changes: 1 addition & 1 deletion authorityspoke/opinions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from dataclasses import dataclass, field

from anchorpoint.textselectors import TextQuoteSelector, TextPositionSelector
from justopinion.decisions import CAPOpinion as Opinion
from justopinion.decisions import Opinion
from nettlesome.terms import Comparable, ContextRegister, Explanation
from nettlesome.factors import Factor
from pydantic import BaseModel, Field, validator
Expand Down
15 changes: 8 additions & 7 deletions docs/guides/create_holding_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ Next, we can download the judicial decisions we’re going to compare.
... lotus_download = load_decision("lotus_h.json")

Then we convert the JSON responses from the API
into :class:`authorityspoke.opinions.Opinion` objects.
into :class:`authorityspoke.decisions.DecisionReading` objects.

>>> from authorityspoke import Decision
>>> oracle = Decision(**oracle_download).majority
>>> lotus = Decision(**lotus_download).majority
>>> oracle_decision = Decision(**oracle_download).majority
>>> lotus_decision = Decision(**lotus_download).majority
>>> oracle = DecisionReading(decision=oracle_decision)
>>> lotus = DecisionReading(decision=lotus_decision)

And we need a :class:`~legislice.download.Client` for
accessing legislative provisions.
Expand Down Expand Up @@ -133,11 +135,10 @@ AuthoritySpoke object.
'anchors': 'By statute, a work |must be “original” to qualify| for'}

To compare the input data to the created Python objects, link
the Holdings to the :class:`~authorityspoke.opinions.Opinion` using
the :meth:`~authorityspoke.opinions.Opinion.posit` method. As we look at
the Holdings to the :class:`~authorityspoke.opinions.OpinionReading` using
the :meth:`~authorityspoke.opinions.OpinionReading.posit` method. As we look at
the parts of the JSON file, the code cells will show how fields from the
JSON affect the structure of the :class:`~authorityspoke.holdings.Holding` object.

JSON affect the structure of the :class:`~authorityspoke.holdings.Holding`.
>>> oracle.posit(oracle_holdings)
>>> lotus.posit(lotus_holdings)
>>> print(oracle.holdings[0])
Expand Down
50 changes: 20 additions & 30 deletions docs/guides/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ up <https://authorityspoke.com/account/signup/>`__ for an account and
then obtain a Legislice API key from your account page. The Legislice
API key is not the same as the Caselaw Access Project API key.

As of version 0.4, you mostly have to create your own procedural rule
In the current version, you mostly have to create your own procedural rule
annotations, but the ``example_data`` folder of the `GitHub repository
for AuthoritySpoke <https://github.com/mscarey/AuthoritySpoke>`__
contains example annotations for several cases. The rest of this
Expand Down Expand Up @@ -108,14 +108,14 @@ notebook is running.
>>> from authorityspoke.io.loaders import load_decision
>>> from authorityspoke.io.readers import read_decision
>>> if not USE_REAL_CASE_API:
... oracle_download = load_decision("oracle_h.json")
... lotus_download = load_decision("lotus_h.json")
... oracle_case = read_decision(load_decision("oracle_h.json"))
... lotus_case = read_decision(load_decision("lotus_h.json"))

Downloading and Importing Decisions
--------------------------------------

If you didn’t load court opinions from the GitHub repository as
described in section 1.1, then you’ll be using the Caselaw Access
described above, then you’ll be using the Caselaw Access
Project (CAP) API to get court opinions to load into AuthoritySpoke. To
download full cases from CAP, you’ll need to `register for a CAP API
key <https://case.law/user/register/>`__.
Expand All @@ -130,11 +130,6 @@ ever writing the API key in the notebook. That makes it easier to keep
your API key secret, even if you publish your copy of the notebook and
make it visible on the internet.

However, if you’re viewing this tutorial in a cloud environment like
Binder, you probably won’t be able to create an environment variable.
Instead, you could replace ``os.getenv('CAP_API_KEY')`` with a string
containing your own API key.

>>> import os
>>> from dotenv import load_dotenv
>>> load_dotenv()
Expand All @@ -160,22 +155,22 @@ uncopyrightable because it was a “method of operation” under the
Copyright Act. As we’ll see, the Oracle case discusses and disagrees
with the Lotus case.

If you already loaded an :class:`~authorityspoke.opinions.Opinion`
If you already loaded a :class:`~authorityspoke.decisions.Decision`
from a file, running the cells
below with ``USE_REAL_CASE_API`` set to True will attempt to overwrite
them with data from the API. You should be able to run the rest of the
it with data from the API. You should be able to run the rest of the
tutorial code either way.

>>> from authorityspoke import LegisClient
>>> if USE_REAL_CASE_API:
... client = LegisClient(api_token=CAP_API_KEY)
... oracle_download = client.fetch(cite="750 F.3d 1339")
... case_client = LegisClient(api_token=CAP_API_KEY)
... oracle_case = case_client.read_cite(cite="750 F.3d 1339")

Now we have a record representing the *Oracle* case, which can also be
found in the “example_data/opinions” folder under the filename
“oracle_h.json”. Let’s look at a field from the API response.

>>> oracle_download["name"]
>>> oracle_case.name
'ORACLE AMERICA, INC., Plaintiff-Appellant, v. GOOGLE INC., Defendant-Cross-Appellant'

Yes, this is the correct case name. But if we had provided the API key
Expand All @@ -185,31 +180,23 @@ case, and the names of the opinion authors. So let’s request the
*Oracle* case with ``full_case=True``.

>>> if USE_REAL_CASE_API:
... oracle_download = download_case(
... oracle_case = case_client.read_cite(
... cite="750 F.3d 1339",
... full_case=True,
... api_key=CAP_API_KEY)
... full_case=True)

And then do the same for the *Lotus* case.

>>> if USE_REAL_CASE_API:
... lotus_download = download_case(
... lotus_case = case_client.read_cite(
... cite="49 F.3d 807",
... full_case=True,
... api_key=CAP_API_KEY)

Now let’s convert the *Oracle* API response to an AuthoritySpoke object.

>>> from authorityspoke import Decision
>>> oracle = Decision(**oracle_download)
... full_case=True)

And take a look at the object we made.
Now let’s look at the objects we made.

>>> print(oracle)
>>> print(oracle_case)
Oracle America, Inc. v. Google Inc., 750 F.3d 1339 (2014-05-09)

>>> lotus = read_decision(lotus_download)
>>> print(lotus)
>>> print(lotus_case)
Lotus Development Corp. v. Borland International, Inc., 49 F.3d 807 (1995-03-09)

One judicial :class:`~authorityspoke.decisions.Decision` can include
Expand All @@ -219,7 +206,7 @@ as well as a majority opinion.
Access the ``majority`` attribute of the :class:`~authorityspoke.decisions.Decision`
object to get the majority opinion.

>>> print(lotus.majority)
>>> print(lotus_case.majority)
majority opinion by STAHL, Circuit Judge

Downloading Enactments
Expand Down Expand Up @@ -347,9 +334,12 @@ This will also link the correct text passages from
the :class:`~authorityspoke.opinions.Opinion` to
each :class:`~authorityspoke.holdings.Holding`\.

>>> from authorityspoke import authorityspoke
>>> from authorityspoke.io.loaders import read_anchored_holdings_from_file
>>> oracle_holdings_with_anchors = read_anchored_holdings_from_file("holding_oracle.json", client=legis_client)
>>> lotus_holdings_with_anchors = read_anchored_holdings_from_file("holding_lotus.json", client=legis_client)
>>> oracle = DecisionReading(oracle_holdings_with_anchors)
>>> lotus = DecisionReading(lotus_holdings_with_anchors)
>>> oracle.posit(oracle_holdings_with_anchors)
>>> lotus.posit(lotus_holdings_with_anchors)

Expand Down
8 changes: 5 additions & 3 deletions docs/guides/load_yaml_holdings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ convert them into AuthoritySpoke :class:`~authorityspoke.decisions.Decision` obj
citation of the case we want, we'll use the :meth:`~authorityspoke.io.downloads.CAPClient.read_cite` method.

>>> from authorityspoke.io.downloads import CAPClient
>>> from authorityspoke.decisions import Decision, Opinion, CAPCitation
>>> from authorityspoke.decisions import DecisionReading, Decision, Opinion, CAPCitation
>>> if USE_REAL_CASE_API:
... client = CAPClient(api_token=CAP_API_KEY)
... licensing_case = client.read_cite(
Expand All @@ -65,6 +65,7 @@ citation of the case we want, we'll use the :meth:`~authorityspoke.io.downloads.
... licensing_case.add_opinion(Opinion())
>>> print(licensing_case)
United States v. Mazza-Alaluf, 621 F.3d 205 (2010-09-22)
>>> licensing_case_reading = DecisionReading(decision=licensing_case)

If we had used ``full_case=True``, we would have the option to view the full
text of the majority opinion using the command ``licensing_case.majority.text``.
Expand Down Expand Up @@ -586,11 +587,12 @@ Now when we load a file with this YAML, we'll get both Holdings.
2

Now that we generated this :class:`~authorityspoke.opinions.AnchoredHoldings` object
containing the data from the YAML file, we can use the posit method to link those
containing the data from the YAML file, we can use
the :class:`~authorityspoke.decisions.DecisionReading.posit` method to link those
:class:`~authorityspoke.holdings.Holding`\s to the judicial :class:`~authorityspoke.decisions.Decision`
we created from the data we downloaded from the CAP API. Then we can verify that
those two Holdings are now considered the two holdings of the Decision.

>>> licensing_case.posit(both_holdings_with_anchors)
>>> licensing_case_reading.posit(both_holdings_with_anchors)
>>> len(licensing_case.holdings)
2
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
git+git://github.com/mscarey/anchorpoint.git@824da6bc1b256bdeb02fc51e435e3fdeecf09062#egg=anchorpoint
anchorpoint~=0.5.2
apispec[validation]~=4.3.0
apispec-oneofschema
eyecite~=2.2.0
git+git://github.com/mscarey/justopinion.git@12ab0468d2d8036d3c733d73cdd01a5710d5d814#egg=justopinion
-git+git:/github.com/mscarey/justopinion.git@4999049584194de86d75d2f4103e8d06b4b41d24#egg=justopinion
legislice>=0.5.2
marshmallow>=3.10
marshmallow-oneofschema
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from anchorpoint.textselectors import TextQuoteSelector
from dotenv import load_dotenv
from justopinion.decisions import CAPDecision, CAPOpinion
from justopinion.decisions import Decision, Opinion
from legislice.download import Client
from legislice.yaml_schemas import ExpandableEnactmentSchema as EnactmentSchema
from nettlesome.terms import ContextRegister
Expand Down
6 changes: 3 additions & 3 deletions tests/io/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import eyecite
import pytest

from justopinion.decisions import CAPDecision
from justopinion.decisions import Decision

from authorityspoke.io.downloads import CAPClient, CaseAccessProjectAPIError
from authorityspoke import LegisClient, DecisionReading
Expand Down Expand Up @@ -51,7 +51,7 @@ def test_download_and_make_opinion(self):
)
lotus = response[0]
lotus_opinion = lotus.majority
assert lotus_opinion.__class__.__name__ == "CAPOpinion"
assert lotus_opinion.__class__.__name__ == "Opinion"

@pytest.mark.vcr
def test_download_case_by_cite(self):
Expand All @@ -77,7 +77,7 @@ def test_download_save_and_make_opinion(self, tmp_path):
filepath = tmp_path / to_file
lotus_from_file = load_decision(filepath=filepath)
lotus = Decision(**lotus_from_file)
assert lotus.majority.__class__.__name__ == "CAPOpinion"
assert lotus.majority.__class__.__name__ == "Opinion"
assert "Lotus" in lotus.name_abbreviation

def test_error_download_without_case_reference(self):
Expand Down

0 comments on commit 32aef31

Please sign in to comment.