def read_holding(record: RawHolding, regime: Optional[Regime] = None) -> Holding: r""" Create new :class:`Holding` object from simple datatypes from JSON input. Will yield multiple items if ``exclusive: True`` is present in ``record``. :param record: dict of values for constructing :class:`.Holding` :param regime: Collection of :class:`.Jurisdiction`\s and corresponding :class:`.Code`\s for discovering :class:`.Enactment`\s to reference in the new :class:`Holding`. :param many: if True, record represents a list of :class:`Holding`\s rather than just one. :returns: New :class:`.Holding`, and an updated dictionary with mentioned :class:`.Factor`\s as keys and their :class:`.TextQuoteSelector`\s as values. """ schema = schemas.HoldingSchema(many=False) record, schema.context["mentioned"] = index_names(record) schema.context["regime"] = regime return schema.load(deepcopy(record))
def read_enactments( record: List[RawEnactment], code: Optional[Code] = None, regime: Optional[Regime] = None, ) -> List[Enactment]: r""" Create a new :class:`Enactment` object using imported JSON data. The new :class:`Enactment` can be composed from a :class:`.Code` referenced in the ``regime`` parameter. :param record: sequence of :class:`dict`\s with string fields from JSON for constructing new :class:`.Enactment`\s :param code: a :class:`.Code` that is the source for every :class:`Enactment` to be loaded, if they all come from the same :class:`.Code` :param regime: the :class:`.Regime` where the :class:`.Code`\s that are the source for this :class:`Enactment` can be found, or where it should be added :returns: a list of new :class:`Enactment` objects, optionally with text links. """ schema = schemas.EnactmentSchema(many=True) record, schema.context["mentioned"] = index_names(record) schema.context["regime"] = regime schema.context["code"] = code return schema.load(deepcopy(record))
def read_enactment(record: RawEnactment, code: Optional[Code] = None, regime: Optional[Regime] = None) -> Enactment: r""" Create a new :class:`.Enactment` object using imported JSON data. The new :class:`.Enactment` can be composed from a :class:`.Code` referenced in the ``regime`` parameter. :param record: :class:`dict` with string fields from JSON for constructing new :class:`.Enactment` :param code: the :class:`.Code` that is the source for this :class:`Enactment` :param regime: the :class:`.Regime` where the :class:`.Code` that is the source for this :class:`Enactment` can be found, or where it should be added :returns: a new :class:`Enactment` object, optionally with text links. """ schema = schemas.EnactmentSchema(many=False) record, schema.context["mentioned"] = index_names(record) schema.context["regime"] = regime schema.context["code"] = code return schema.load(deepcopy(record))
def extract_anchors_from_holding_record( record: List[RawHolding], client: Optional[Client] = None ) -> Tuple[List[Holding], List[EnactmentWithAnchors], List[TermWithAnchors], List[Dict[str, str]], ]: r""" Load a list of Holdings from JSON, with text links. :param record: a list of dicts representing holdings, in the JSON input format :param client: Legislice client for downloading missing fields from `record` :returns: a tuple of four objects containing holdings, terms, enactments, and anchors. """ record_post_enactments, enactment_index = collect_enactments(record) if client: enactment_index_post_client = client.update_entries_in_enactment_index( enactment_index) else: enactment_index_post_client = enactment_index enactment_anchors, enactment_index_post_anchors = collect_anchors_from_index( enactment_index_post_client, "passage") enactment_result = [] for anchor in enactment_anchors: anchor["passage"] = enactment_index_post_anchors.get_if_present( anchor["passage"]) enactment_result.append(EnactmentWithAnchors(**anchor)) record_post_terms, factor_index = index_names(record_post_enactments) factor_anchors, factor_index_post_anchors = collect_anchors_from_index( factor_index, "term") factor_result = [] for anchor in factor_anchors: anchor["term"] = expand_holding( anchor["term"], factor_index=factor_index_post_anchors, enactment_index=enactment_index_post_anchors, ) factor_result.append(TermWithAnchors(**anchor)) factor_anchors = [TermWithAnchors(**anchor) for anchor in factor_anchors] expanded = expand_holdings( record_post_terms, factor_index=factor_index_post_anchors, enactment_index=enactment_index_post_anchors, ) holding_anchors = [holding.pop("anchors", None) for holding in expanded] result = [] for holding in expanded: result.append(Holding(**holding)) return result, enactment_result, factor_result, holding_anchors
def test_enactment_name_index(self, make_regime): """ Test error message: 'Name "securing for authors" not found in the index of mentioned Factors' """ feist_records = loaders.load_holdings("holding_feist.json") record, mentioned = name_index.index_names(feist_records) assert "securing for authors" in mentioned
def read_rules_with_index(record: List[RawRule], regime: Optional[Regime] = None, many: bool = True) -> Tuple[List[Rule], Mentioned]: r"""Make :class:`Rule` and "mentioned" index from dict of fields and :class:`.Regime`\.""" record, mentioned = index_names(record) schema = schemas.RuleSchema(many=many) schema.context["regime"] = regime schema.context["mentioned"] = mentioned rules = schema.load(deepcopy(record)) return rules, mentioned
def test_import_predicate_with_quantity(self): record = expand_shorthand(self.story_data) record, mentioned = index_names(record) expanded = readers.expand_factor(record, mentioned) story = Fact(**expanded) assert len(story.predicate) == 1 assert story.predicate.content.startswith("The number of castles") assert story.predicate.sign == ">" assert story.predicate.quantity == 3
def read_holdings_with_index(record: List[RawHolding], regime: Optional[Regime] = None, many: bool = True) -> HoldingsIndexed: r"""Load a list of :class:`Holdings`\s from JSON, with "mentioned" index.""" record, mentioned = index_names(record) schema = schemas.HoldingSchema(many=many) schema.context["regime"] = regime schema.context["mentioned"] = mentioned anchor_list = anchors.get_holding_anchors(record) holdings = schema.load(deepcopy(record)) return HoldingsIndexed(holdings, mentioned, anchor_list)
def test_enactment_with_anchor(self, make_regime): record, mentioned = name_index.index_names(self.test_enactments[1]) schema = schemas.EnactmentSchema(many=False) schema.context["mentioned"] = mentioned schema.context["regime"] = make_regime enactment = schema.load(record) factor_anchors = anchors.get_named_anchors(mentioned) assert enactment.text.startswith( "nor shall any State deprive any person of life, liberty, or property" ) assert (factor_anchors[enactment.name][0].exact == "reference to the Due Process Clause")
def test_link_longest_context_factors_first(self): """ If read_holdings interprets the second "Bradley" string as a reference to Bradley rather than "Bradley's house", it's wrong. """ to_read = { "outputs": [ {"type": "fact", "content": "{Bradley's house} was a house"}, {"type": "fact", "content": "{Bradley} lived at Bradley's house"}, ] } record, mentioned = name_index.index_names(to_read) lived_at = record["outputs"][1] assert mentioned[lived_at]["context_factors"][1] == "Bradley's house"
def read_fact(record: RawFactor) -> Fact: r""" Construct a :class:`Fact` after loading a dict from JSON. :param record: parameter values to pass to :class:`.FactSchema`\. :returns: a :class:`Fact`, with optional mentioned factors """ record, mentioned = index_names(record) schema = schemas.FactSchema() schema.context["mentioned"] = mentioned return schema.load(record)
def read_factors(record: List[RawFactor], regime: Optional[Regime] = None, **kwargs) -> Factor: r""" Turn fields from JSON into a :class:`Factor` object. :param record: parameter values to pass to schema :parame regime: to look up any :class:`.Enactment` references """ schema = schemas.FactorSchema(many=True) record, schema.context["mentioned"] = index_names(record) schema.context["regime"] = regime return schema.load(record)
def read_rule(record: Dict, regime: Optional[Regime] = None) -> Rule: r""" Make :class:`Rule` from a :class:`dict` of fields and a :class:`.Regime`\. :param record: :param regime: :returns: iterator yielding :class:`Rule`\s with the items from ``mentioned_entities`` as ``context_factors`` """ record, mentioned = index_names(record) schema = schemas.RuleSchema() schema.context["mentioned"] = mentioned schema.context["regime"] = regime return schema.load(record)
def read_procedure(record: Dict, regime: Optional[Regime] = None, many=False) -> Procedure: r""" Turn fields from JSON into a :class:`Procedure` object. :param record: parameter values to pass to schema :param many: whether to use the "many" form of the Marshmallow schema (whether there are multiple Procedures) :parame regime: to look up any :class:`.Enactment` references """ schema = schemas.ProcedureSchema(many=many) record, schema.context["mentioned"] = index_names(record) schema.context["regime"] = regime return schema.load(record)
def test_make_fact_from_string(self, watt_factor): fact_float_data = { "content": "the distance between $person0 and $person1 was >= 20.1", "terms": [ { "type": "Entity", "name": "Ann" }, { "type": "Entity", "name": "Lee" }, ], } record = expand_shorthand(fact_float_data) record, mentioned = index_names(record) expanded = readers.expand_factor(record, mentioned) fact_float_more = Fact(**expanded) fact_float_less = watt_factor["f8_int"] assert fact_float_more >= fact_float_less
def test_index_before_apostrophe(self): """ Not catching the error where 'Bradley exhibited an expectation of privacy in Bradley's marijuana patch' becomes '{} exhibited an expectation of privacy in {{}' """ raw_holding = { "outputs": [ { "type": "Fact", "content": "the {possessive noun}'s factor was linked correctly", }, { "type": "Fact", "content": "the possessive noun's factor was still linked correctly", }, ], } holding, mentioned = name_index.index_names(raw_holding) factor_name_1 = holding["outputs"][0] assert mentioned[factor_name_1]["context_factors"][0] == "possessive noun" factor_name_2 = holding["outputs"][1] assert mentioned[factor_name_2]["context_factors"][0] == "possessive noun"
def read_holdings( record: List[RawHolding], regime: Optional[Regime] = None, code: Optional[Code] = None, ) -> List[Holding]: r""" Load a list of :class:`Holdings`\s from JSON. :param record: a list of dicts representing holdings, in the JSON input format :parame regime: A collection of :class:`.Jurisdiction`\s and the :class:`.Code`\s that have been enacted in each. Used for constructing :class:`.Enactment`\s referenced by :class:`.Holding`\s. :returns: a list of :class:`.Holding` objects """ schema = schemas.HoldingSchema(many=True) record, schema.context["mentioned"] = index_names(record) schema.context["regime"] = regime schema.context["code"] = code return schema.load(deepcopy(record))
def test_index_names_from_sibling_inputs(self, make_regime): raw_rules = loaders.load_holdings("beard_rules.json") indexed_rules, mentioned = name_index.index_names( raw_rules[0]["inputs"]) key = "the suspected beard occurred on or below the chin" assert mentioned[key]["context_factors"][0] == "the suspected beard"
def test_mentioned_ordered_by_length(self, raw_factor): obj = text_expansion.expand_shorthand(raw_factor["relevant"]) obj, mentioned = name_index.index_names(obj) shortest = mentioned.popitem() assert shortest[0] == "Short Name"
def test_index_names_turns_context_factor_str_into_list(self, raw_factor): short_shot_long, mentioned = name_index.index_names( raw_factor["relevant"]["context_factors"][0] ) assert isinstance(mentioned[short_shot_long]["context_factors"], list)
def test_index_names(self, raw_factor): obj, mentioned = name_index.index_names(raw_factor["relevant"]) factor = mentioned[obj]["context_factors"][0] assert mentioned[factor]["context_factors"][0] == "Short Name"
def test_import_enactments_and_anchors(self, make_regime, make_opinion): """ Testing issue that caused enactment expansion to fail only when text anchors were loaded. """ raw_holdings = [ { "inputs": { "type": "fact", "content": "{Rural's telephone directory} was a fact", "anchors": [ {"exact": "facts", "prefix": "The first is that"}, { "exact": "as to facts", "prefix": "No one may claim originality", }, {"exact": "facts", "prefix": "no one may copyright"}, ], }, "outputs": { "type": "fact", "content": "Rural's telephone directory was copyrightable", "truth": False, "anchors": [ { "exact": "are not copyrightable", "prefix": "The first is that facts", }, {"exact": "no one may copyright", "suffix": "facts"}, ], }, "enactments": [ { "source": "/us/const/article-I/8/8", "exact": ( "To promote the Progress of Science and useful Arts, " "by securing for limited Times to Authors" ), "name": "securing for authors", }, { "source": "/us/const/article-I/8/8", "exact": "the exclusive Right to their respective Writings", "name": "right to writings", }, ], "mandatory": True, "universal": True, }, { "outputs": { "type": "fact", "content": "Rural's telephone directory was copyrightable", "anchors": [ { "exact": "copyrightable", "prefix": "first is that facts are not", }, "The sine qua non of|copyright|", ], }, "enactments": ["securing for authors", "right to writings"], "mandatory": True, "anchors": "compilations of facts|generally are|", }, ] record, mentioned = name_index.index_names(raw_holdings) holding_anchors = anchors.get_holding_anchors(record) named_anchors = anchors.get_named_anchors(mentioned) schema = schemas.HoldingSchema(many=True) schema.context["mentioned"] = mentioned schema.context["regime"] = make_regime feist_holdings = schema.load(record) feist = make_opinion["feist_majority"] feist.posit( feist_holdings, holding_anchors=holding_anchors, named_anchors=named_anchors ) assert feist.holdings[0].enactments[0].name == "securing for authors" assert feist.holdings[1].enactments[0].name == "securing for authors"
def test_index_names_from_otherwise_identical_factors(self): expanded, mentioned = name_index.index_names(self.smith_holdings) fact = mentioned[expanded[1]["inputs"][0]] assert fact["context_factors"][0] == "Smythe"
def test_anchors_from_fact_with_inferred_name(self): record, mentioned = index_names(self.fact) factor_anchors = anchors.get_named_anchors(mentioned) fact_anchors = factor_anchors[ "false Rural's telephone directory was copyrightable"] assert fact_anchors[1].exact == "no one may copyright"
def test_anchor_not_overwritten_when_indexing(self, raw_holding): watch = raw_holding["stolen watch"] record, mentioned = name_index.index_names(watch) assert len(mentioned["Mark stole a watch"]["anchors"]) == 2
def test_make_enactment_anchor(self): record, mentioned = index_names(self.enactment_anchor) named_anchors = anchors.get_named_anchors(mentioned) enactment_anchors = named_anchors["copyright protection provision"] assert enactment_anchors[0].exact == "17 U.S.C. § 102(a)"
def test_import_fact_with_entity_name_containing_another(self): expanded = expand_shorthand(self.house_data) record, mentioned = index_names(expanded) assert mentioned["Alice's house"]["type"] == "Entity"
def test_index_names_from_sibling_inputs(self): raw_rules = loaders.load_holdings("beard_rules.yaml") indexed_rules, mentioned = name_index.index_names( raw_rules[0]["inputs"]) key = "the suspected beard occurred on or below the chin" assert mentioned[key]["terms"][0] == "the suspected beard"