Skip to content

Commit

Permalink
remove Entity subclasses
Browse files Browse the repository at this point in the history
Designating an object to be a member of an Entity subclass such as Association, rather than just an Entity, seems to be equivalent to creating a Fact that "X was an association", except that I don't know how to reason about a subclass relationship in all the ways I can reason about a Fact object.
  • Loading branch information
mscarey committed Jun 9, 2019
1 parent 6873b7f commit 8f86422
Show file tree
Hide file tree
Showing 14 changed files with 44 additions and 114 deletions.
45 changes: 0 additions & 45 deletions authorityspoke/entities.py

This file was deleted.

9 changes: 7 additions & 2 deletions authorityspoke/factors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Any, Callable, ClassVar, Dict, Iterable, Iterator, List
from typing import Optional, Sequence, Set, Tuple, Type, Union

from dataclasses import dataclass
from dataclasses import astuple, dataclass

from authorityspoke.context import log_mentioned_context
from authorityspoke.predicates import Predicate
Expand Down Expand Up @@ -1084,7 +1084,12 @@ def means(left: Factor, right: Factor) -> bool:
@dataclass(frozen=True)
class Entity(Factor):
"""
A person, place, thing, or event that needs to be mentioned in
Things that exist in the outside world, like people, places, or events.
Not concepts that derive their meaning from litigation,
such as a legal Fact, an Allegation, a Pleading, etc.
Best used to specify things to be mentioned in
multiple :class:`.Factor`\s in a :class:`.Rule`, often in the
:class:`.Predicate` of a :class:`.Fact` object.
Expand Down
4 changes: 2 additions & 2 deletions example_data/holdings/holding_brad.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"name": "Bradley's house"
},
{
"type": "human",
"type": "entity",
"name": "Bradley"
},
{
"type": "entity",
"name": "Bradley's marijuana patch"
},
{
"type": "event",
"type": "entity",
"name": "officers' search of the yard"
},
{
Expand Down
4 changes: 2 additions & 2 deletions example_data/holdings/holding_cardenas.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"mentioned_factors": [{
"type": "Human",
"type": "entity",
"name": "defendant"
},
{
"type": "Human",
"type": "entity",
"name": "parole officer"
},
{
Expand Down
4 changes: 2 additions & 2 deletions example_data/holdings/holding_lotus.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"mentioned_factors": [

{
"type": "association",
"type": "entity",
"name": "Lotus Development Corporation"
},
{
"type": "association",
"type": "entity",
"name": "Borland International"
},
{
Expand Down
6 changes: 3 additions & 3 deletions example_data/holdings/holding_oracle.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"mentioned_factors": [

{
"type": "association",
"type": "entity",
"name": "Oracle America"
},
{
"type": "association",
"type": "entity",
"name": "Google"
},
{
"type": "association",
"type": "entity",
"name": "Sun Microsystems"
},
{
Expand Down
4 changes: 2 additions & 2 deletions example_data/holdings/holding_watt.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"name": "Hideaway Lodge"
},
{
"type": "human",
"type": "entity",
"name": "Wattenburg"
},
{
"type": "entity",
"name": "the stockpile of trees"
},
{
"type": "event",
"type": "entity",
"name": "officers' search of the stockpile"
},
{
Expand Down
27 changes: 8 additions & 19 deletions notebooks/introduction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -252,18 +252,7 @@
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Code(\"cfr37.xml\")"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"from authorityspoke import Code, Regime\n",
"\n",
Expand Down Expand Up @@ -312,23 +301,23 @@
"source": [
"Before we see what that did, let's look at the format of the holding JSON files we just loaded in.\n",
"\n",
"The top level of each of these JSON files has two fields: `\"mentioned_factors\"` and `\"holdings\"`. `\"mentioned_factors\"` is where you list the people, places, and things that need to be mentioned more than once to explain a holding. The reason you're listing these entities is so AuthoritySpoke will recognize that the same entities have been mentioned more than once in different contexts. Each of these entities has a `\"type\"` attribute, which will be the Python class name for the object that's created. You can use the generic type \"Entity\", or the more specific subtypes \"Association\", \"Human\", or \"Event\".\n",
"The top level of each of these JSON files has two fields: `\"mentioned_factors\"` and `\"holdings\"`. `\"mentioned_factors\"` is where you list the people, places, and things that need to be mentioned more than once to explain a holding. The reason you're listing these entities is so AuthoritySpoke will recognize that the same entities have been mentioned more than once in different contexts. Each of these entities has a `\"type\"` attribute, which will be the Python class name for the object that's created. You can use the generic type \"Entity\".\n",
"\n",
"Here's the `\"mentioned_factors\"` field from holding_oracle.json.\n",
"\n",
"```\n",
"\"mentioned_factors\": [\n",
"\n",
" {\n",
" \"type\": \"association\",\n",
" \"type\": \"entity\",\n",
" \"name\": \"Oracle America\"\n",
" },\n",
" {\n",
" \"type\": \"association\",\n",
" \"type\": \"entity\",\n",
" \"name\": \"Google\"\n",
" },\n",
" {\n",
" \"type\": \"association\",\n",
" \"type\": \"entity\",\n",
" \"name\": \"Sun Microsystems\"\n",
" },\n",
" {\n",
Expand Down Expand Up @@ -589,7 +578,7 @@
{
"data": {
"text/plain": [
"[Association(name='Borland International', generic=True, plural=False),\n",
"[Entity(name='Borland International', generic=True, plural=False),\n",
" Entity(name='the Lotus menu command hierarchy', generic=True, plural=False)]"
]
},
Expand Down Expand Up @@ -617,10 +606,10 @@
"metadata": {},
"outputs": [],
"source": [
"from authorityspoke import Entity, Association\n",
"from authorityspoke import Entity\n",
"\n",
"nosferatu_rule = lotus_majority.holdings[0].new_context(\n",
" {Association('Borland International'): Association('Prana Film'),\n",
" {Entity('Borland International'): Entity('Prana Film'),\n",
" Entity('the Lotus menu command hierarchy'): Entity(\"Dracula\")}\n",
")"
]
Expand Down
15 changes: 7 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from authorityspoke.factors import Factor, Entity
from authorityspoke.enactments import Code, Enactment
from authorityspoke.entities import Event, Human
from authorityspoke.factors import Evidence, Exhibit
from authorityspoke.factors import Fact
from authorityspoke.jurisdictions import Jurisdiction, Regime
Expand All @@ -20,17 +19,17 @@ def make_entity() -> Dict[str, Entity]:
return {
"motel": Entity("Hideaway Lodge"),
"motel_specific": Entity("Hideaway Lodge", generic=False),
"watt": Human("Wattenburg"),
"watt": Entity("Wattenburg"),
"trees": Entity("the stockpile of trees"),
"trees_specific": Entity("the stockpile of trees", generic=False),
"tree_search": Event("officers' search of the stockpile of trees"),
"tree_search_specific": Event(
"tree_search": Entity("officers' search of the stockpile of trees"),
"tree_search_specific": Entity(
"officers' search of the stockpile of trees", generic=False
),
"alice": Human("Alice"),
"bob": Human("Bob"),
"craig": Human("Craig"),
"dan": Human("Dan"),
"alice": Entity("Alice"),
"bob": Entity("Bob"),
"craig": Entity("Craig"),
"dan": Entity("Dan"),
"circus": Entity("circus"),
}

Expand Down
1 change: 0 additions & 1 deletion tests/test_enactments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from pint import UnitRegistry
import pytest

from authorityspoke.entities import Human, Event
from authorityspoke.enactments import Code, Enactment
from authorityspoke.opinions import Opinion
from authorityspoke.predicates import ureg, Q_
Expand Down
20 changes: 4 additions & 16 deletions tests/test_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from authorityspoke.factors import Entity
from authorityspoke.factors import Predicate, Factor, Fact
from authorityspoke.entities import Human
from authorityspoke.opinions import Opinion
from authorityspoke.predicates import ureg, Q_

Expand All @@ -30,20 +29,20 @@ def test_repr_equal_after_make_generic(self, make_entity):

def test_context_register(self, make_entity):
"""
Class "Human" implies "Entity" but not vice versa.
There will be a match because both object are :class:`.Entity`.
"""
motel = make_entity["motel"]
watt = make_entity["watt"]
empty_update = motel._context_register(watt, operator.ge)
assert not any(register is not None for register in empty_update)
update = motel._context_register(watt, operator.ge)
assert any(register is not None for register in update)

update = motel._context_register(watt, operator.le)
assert any(register == {motel: watt, watt: motel} for register in update)

def test_new_context(self, make_entity):
changes = {
make_entity["motel"]: Entity("Death Star"),
make_entity["watt"]: Human("Darth Vader"),
make_entity["watt"]: Entity("Darth Vader"),
}
motel = make_entity["motel"]
assert motel.new_context(changes) == changes[make_entity["motel"]]
Expand All @@ -62,14 +61,6 @@ def test_equality_generic_entities(self, make_entity):
assert e["motel"].means(e["trees"])
assert not e["motel"] == e["trees"]

def test_generic_human_and_event_not_equal(self, make_entity):
"""Neither is a subclass of the other."""
assert not make_entity["tree_search"].means(make_entity["watt"])

def test_generic_human_and_entity_not_equal(self, make_entity):
"""Human is a subclass of Entity."""
assert not make_entity["motel"].means(make_entity["watt"])

# Implication

def test_implication_generic_entities(self, make_entity):
Expand All @@ -87,9 +78,6 @@ def test_implication_subclass(self, make_entity):
assert make_entity["tree_search_specific"] >= make_entity["motel"]
assert make_entity["tree_search"] > make_entity["motel"]

def test_implication_superclass(self, make_entity):
assert not make_entity["trees"] >= make_entity["tree_search"]

# Contradiction

def test_error_contradiction_with_non_factor(self, make_entity, make_predicate):
Expand Down
13 changes: 5 additions & 8 deletions tests/test_factors.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pytest

from authorityspoke.factors import Entity, Factor, Fact, means
from authorityspoke.entities import Human
from authorityspoke.rules import Rule, ProceduralRule
from authorityspoke.opinions import Opinion
from authorityspoke.predicates import ureg, Q_
Expand Down Expand Up @@ -95,7 +94,7 @@ def test_string_representation_with_concrete_entities(self, watt_factor):

def test_new_context_replace_fact(self, make_entity, watt_factor):
changes = {
make_entity["watt"]: Human("Darth Vader"),
make_entity["watt"]: Entity("Darth Vader"),
watt_factor["f2"]: watt_factor["f10"],
}
assert "was within the curtilage of <Hideaway Lodge>" in str(
Expand All @@ -108,7 +107,7 @@ def test_get_factor_from_recursive_search(self, make_opinion_with_holding):
)
factor = factor_list[1]
assert any(
factor == Human("parole officer") and factor.name == "parole officer"
factor == Entity("parole officer") and factor.name == "parole officer"
for factor in factor_list
)

Expand Down Expand Up @@ -198,16 +197,14 @@ def test_standard_of_proof_in_str(self, watt_factor):
factor = watt_factor["f2_preponderance_of_evidence"]
assert factor.standard_of_proof in str(factor)

def test_context_register_empty(self, watt_factor):
def test_context_register_empty(self, make_complex_fact, watt_factor):
"""
Yields no context_register because the Entity in f1 doesn't imply
the Human in f1_entity_order.
the Fact in f_relevant_murder.
"""
with pytest.raises(StopIteration):
next(
watt_factor["f1"]._context_registers(
watt_factor["f1_entity_order"], operator.ge
)
watt_factor["f1"]._context_registers(make_complex_fact["f_relevant_murder"], operator.ge)
)

def test_context_register_valid(self, make_entity, watt_factor):
Expand Down
3 changes: 1 addition & 2 deletions tests/test_notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"""

from authorityspoke import Enactment, Entity
from authorityspoke.entities import Association
from authorityspoke.selectors import TextQuoteSelector


Expand All @@ -21,7 +20,7 @@ def test_replace_generic_factor(self, make_opinion_with_holding):
lotus_majority = make_opinion_with_holding["lotus_majority"]
nosferatu_rule = lotus_majority.holdings[0].new_context(
{
Association("Borland International"): Association("Prana Film"),
Entity("Borland International"): Entity("Prana Film"),
Entity("the Lotus menu command hierarchy"): Entity("Dracula"),
}
)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import pytest

from authorityspoke.enactments import Code, Enactment
from authorityspoke.entities import Human
from authorityspoke.factors import Predicate, Entity, Factor, Fact
from authorityspoke.factors import Evidence, Exhibit
from authorityspoke.rules import Procedure, Rule, ProceduralRule
Expand All @@ -26,7 +25,7 @@ def test_None_not_in_str(self, make_holding):

def test_new_concrete_context(self, make_holding):
different = make_holding["h1"].new_context(
[Entity("Castle Grayskull"), Human("He-Man")]
[Entity("Castle Grayskull"), Entity("He-Man")]
)
assert "<He-Man> operated" in str(different)

Expand Down

0 comments on commit 8f86422

Please sign in to comment.