def test_process_amendments_context(monkeypatch): """Context should carry over between REGTEXTs""" # turn on the interpretations plugin monkeypatch.setattr( fetch, 'instantiate_if_possible', Mock(return_value=[ appendix.content_for_appendix, section.content_for_regtext ])) amdpar1 = "3. In § 106.1, revise paragraph (a) to read as follows:" amdpar2 = "3. Add appendix C" with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(TITLE="12"): ctx.AMDPAR(amdpar1) with ctx.SECTION(): ctx.SECTNO("§ 106.1") ctx.SUBJECT("Some Subject.") ctx.P("(a) Something new") with ctx.REGTEXT(TITLE="12"): ctx.AMDPAR(amdpar2) ctx.HD("Appendix C to Part 106", SOURCE="HD1") with ctx.EXTRACT(): ctx.P("Text") preprocess_amdpars(ctx.xml) amd1, amd2 = fetch.fetch_amendments(ctx.xml) assert amd1['instruction'] == amdpar1 assert amd1['cfr_part'] == '106' assert amd2['instruction'] == amdpar2 assert amd2['cfr_part'] == '106' assert ['106-1-a'] == [c[0] for c in amd1['changes']] assert ['106-C', '106-C-p1'] == list(sorted(c[0] for c in amd2['changes']))
def test_multiple_changes(monkeypatch): """ A notice can have two modifications to a paragraph. """ amdpar1 = ("2. Designate §§ 106.1 through 106.3 as subpart A under " "the heading.") amdpar2 = "3. In § 106.2, revise the introductory text to read:" with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(PART="106", TITLE="12"): ctx.AMDPAR(amdpar1) with ctx.REGTEXT(PART="106", TITLE="12"): ctx.AMDPAR(amdpar2) with ctx.SECTION(): ctx.SECTNO("§ 106.2") ctx.SUBJECT(" Definitions ") ctx.P(" Except as otherwise provided, the following " "apply. ") preprocess_amdpars(ctx.xml) amd1, amd2 = fetch.fetch_amendments(ctx.xml) changes1, changes2 = dict(amd1['changes']), dict(amd2['changes']) assert amd1['instruction'] == amdpar1 assert amd1['cfr_part'] == '106' assert amd2['instruction'] == amdpar2 assert amd2['cfr_part'] == '106' assert len(changes1['106-2']) == 1 assert len(changes2['106-2']) == 1
def test_process_amendments_subpart(monkeypatch): with XMLBuilder("RULE") as ctx: with ctx.REGTEXT(PART="105", TITLE="12"): ctx.AMDPAR("3. In § 105.1, revise paragraph (b) to read as" "follows:") with ctx.SECTION(): ctx.SECTNO("§ 105.1") ctx.SUBJECT("Purpose.") ctx.STARS() ctx.P("(b) This part carries out.") with ctx.REGTEXT(PART="105", TITLE="12"): ctx.AMDPAR("6. Add subpart B to read as follows:") with ctx.CONTENTS(): with ctx.SUBPART(): ctx.SECHD("Sec.") ctx.SECTNO("105.30") ctx.SUBJECT("First In New Subpart.") with ctx.SUBPART(): ctx.HD("Subpart B—Requirements", SOURCE="HED") with ctx.SECTION(): ctx.SECTNO("105.30") ctx.SUBJECT("First In New Subpart") ctx.P("For purposes of this subpart, the follow " "apply:") ctx.P('(a) "Agent" means agent.') preprocess_amdpars(ctx.xml) subpart_amendment = fetch.fetch_amendments(ctx.xml)[1] changes = dict(subpart_amendment['changes']) assert '105-Subpart-B' in changes assert '105-30-a' in changes assert '105-30' in changes
def test_process_amendments_multiple_sections(): """Regression test verifying multiple SECTIONs in the same REGTEXT""" amdpar1 = "1. Modify § 111.22 by revising paragraph (b)" amdpar2 = "2. Modify § 111.33 by revising paragraph (c)" with XMLBuilder("REGTEXT", PART="111") as ctx: ctx.AMDPAR(amdpar1) with ctx.SECTION(): ctx.SECTNO("§ 111.22") ctx.SUBJECT("Subject Here.") ctx.STARS() ctx.P("(b) Revised second paragraph") ctx.AMDPAR(amdpar2) with ctx.SECTION(): ctx.SECTNO("§ 111.33") ctx.SUBJECT("Another Subject") ctx.STARS() ctx.P("(c) Revised third paragraph") preprocess_amdpars(ctx.xml) amd1, amd2 = fetch.fetch_amendments(ctx.xml) assert amd1['instruction'] == amdpar1 assert amd1['cfr_part'] == '111' assert ['111-22-b'] == [c[0] for c in amd1['changes']] assert amd2['instruction'] == amdpar2 assert amd2['cfr_part'] == '111' assert ['111-33-c'] == [c[0] for c in amd2['changes']]
def test_process_amendments_mix_regs(): """Some notices apply to multiple regs. For now, just ignore the sections not associated with the reg we're focused on""" amdpar1 = "3. In § 105.1, revise paragraph (a) to read as follows:" amdpar2 = "3. In § 106.3, revise paragraph (b) to read as follows:" with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(PART="105", TITLE="12"): ctx.AMDPAR(amdpar1) with ctx.SECTION(): ctx.SECTNO("§ 105.1") ctx.SUBJECT("105Purpose.") ctx.P("(a) 105Content") with ctx.REGTEXT(PART="106", TITLE="12"): ctx.AMDPAR(amdpar2) with ctx.SECTION(): ctx.SECTNO("§ 106.3") ctx.SUBJECT("106Purpose.") ctx.P("(b) Content") preprocess_amdpars(ctx.xml) amd1, amd2 = fetch.fetch_amendments(ctx.xml) assert amd1['instruction'] == amdpar1 assert amd1['cfr_part'] == '105' assert amd2['instruction'] == amdpar2 assert amd2['cfr_part'] == '106' assert ['105-1-a'] == [c[0] for c in amd1['changes']] assert ['106-3-b'] == [c[0] for c in amd2['changes']]
def test_process_amendments_restart_new_section(monkeypatch): # turn on the interpretations plugin monkeypatch.setattr(fetch, 'instantiate_if_possible', Mock(return_value=[ amendments.content_for_interpretations, section.content_for_regtext ])) amdpar1 = "1. In Supplement I to Part 104, comment 22(a) is added" amdpar2 = "3. In § 105.1, revise paragraph (b) to read as follows:" with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(PART="104", TITLE="12"): ctx.AMDPAR(amdpar1) ctx.HD("SUPPLEMENT I", SOURCE='HED') ctx.HD("22(a)", SOURCE='HD1') ctx.P("1. Content") with ctx.REGTEXT(PART="105", TITLE="12"): ctx.AMDPAR(amdpar2) with ctx.SECTION(): ctx.SECTNO("§ 105.1") ctx.SUBJECT("Purpose.") ctx.STARS() ctx.P("(b) This part carries out.") preprocess_amdpars(ctx.xml) amd1, amd2 = fetch.fetch_amendments(ctx.xml) changes1, changes2 = dict(amd1['changes']), dict(amd2['changes']) assert amd1['instruction'] == amdpar1 assert amd1['cfr_part'] == '104' assert amd2['instruction'] == amdpar2 assert amd2['cfr_part'] == '105' assert '104-22-a-Interp' in changes1 assert '105-1-b' in changes2 assert changes1['104-22-a-Interp'][0]['action'] == 'POST' assert changes2['105-1-b'][0]['action'] == 'PUT'
def test_process_amendments_multiple_in_same_parent(): amdpar1 = "1. In § 105.1, revise paragraph (b) to read as follows:" amdpar2 = "2. Also, revise paragraph (c):" with XMLBuilder("REGTEXT", PART="105", TITLE="12") as ctx: ctx.AMDPAR(amdpar1) ctx.AMDPAR(amdpar2) with ctx.SECTION(): ctx.SECTNO("§ 105.1") ctx.SUBJECT("Purpose.") ctx.STARS() ctx.P("(b) This part carries out.") ctx.P("(c) More stuff") preprocess_amdpars(ctx.xml) amd1, amd2 = fetch.fetch_amendments(ctx.xml) changes1, changes2 = dict(amd1['changes']), dict(amd2['changes']) assert amd1['instruction'] == amdpar1 assert amd1['cfr_part'] == '105' assert amd2['instruction'] == amdpar2 assert amd2['cfr_part'] == '105' assert ['105-1-b'] == list(changes1.keys()) assert ['105-1-c'] == list(changes2.keys()) changes = changes1['105-1-b'][0] assert changes['action'] == 'PUT' assert changes['node']['text'] == '(b) This part carries out.' changes = changes2['105-1-c'][0] assert changes['action'] == 'PUT' assert changes['node']['text'] == '(c) More stuff'
def amendments(self): """Getter for relevent amendments. :rtype: list of amendments """ try: amendments = fetch_amendments(self.xml) except: # noqa logger.error('Unable to fetch amendments for %s', self.version_id) return [] return amendments
def test_process_amendments_no_nodes(): amdpar = "1. In § 104.13, paragraph (b) is removed" with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(PART="104", TITLE="12"): ctx.AMDPAR(amdpar) preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] changes = dict(amendment['changes']) assert amendment['instruction'] == amdpar assert amendment['cfr_part'] == '104' assert '104-13-b' in changes assert changes['104-13-b'][0]['action'] == 'DELETE'
def test_introductory_text(): """ Sometimes notices change just the introductory text of a paragraph (instead of changing the entire paragraph tree). """ with XMLBuilder("REGTEXT", PART="106", TITLE="12") as ctx: ctx.AMDPAR("3. In § 106.2, revise the introductory text to read:") with ctx.SECTION(): ctx.SECTNO("§ 106.2") ctx.SUBJECT(" Definitions ") ctx.P(" Except as otherwise provided, the following apply. ") preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] change = dict(amendment['changes'])['106-2'][0] assert change.get('field') == '[text]'
def test_process_amendments_authority(): amdpar = ('1. The authority citation for 27 CFR Part 555 continues to ' 'read as follows:') auth = '18 U.S.C. 847.' with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(TITLE="27", PART="555"): ctx.AMDPAR(amdpar) with ctx.AUTH(): ctx.HD("Authority:", SOURCE="HED") ctx.P(auth) preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] assert amendment['instruction'] == amdpar assert amendment['cfr_part'] == '555' assert amendment['authority'] == auth assert 'changes' not in amendment
def test_process_amendments_markerless(): amdpar = "1. Revise [label:105-11-p5] as blah" with XMLBuilder("REGTEXT", PART="105", TITLE="12") as ctx: ctx.AMDPAR(amdpar) with ctx.SECTION(): ctx.SECTNO("§ 105.11") ctx.SUBJECT("Purpose.") ctx.STARS() ctx.P("Some text here") preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] changes = dict(amendment['changes']) assert amendment['instruction'] == amdpar assert amendment['cfr_part'] == '105' assert ['105-11-p5'] == list(changes.keys()) changes = changes['105-11-p5'][0] assert changes['action'] == 'PUT'
def test_process_amendments_insert_in_order(): amdpar = '[insert-in-order] [label:123-45-p6]' with XMLBuilder("ROOT") as ctx: with ctx.REGTEXT(TITLE="10"): ctx.AMDPAR(amdpar) with ctx.SECTION(): ctx.SECTNO("§ 123.45") ctx.SUBJECT("Some Subject.") ctx.STARS() ctx.P("This is the sixth paragraph") ctx.STARS() preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] changes = dict(amendment['changes']) assert amendment['instruction'] == amdpar assert amendment['cfr_part'] == '123' assert ['123-45-p6'] == list(changes.keys()) assert changes['123-45-p6'][0]['action'] == 'INSERT'
def test_process_amendments(): amdpar = ("2. Designate §§ 105.1 through 105.3 as subpart A under the " "heading.") with XMLBuilder("REGTEXT", PART="105", TITLE="12") as ctx: with ctx.SUBPART(): ctx.HD("Subpart A—General", SOURCE="HED") ctx.AMDPAR(amdpar) preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] changes = dict(amendment['changes']) assert amendment['instruction'] == amdpar assert amendment['cfr_part'] == '105' assert ['105-1', '105-2', '105-3'] == list(sorted(changes.keys())) for change_list in changes.values(): assert len(change_list) == 1 change = change_list[0] assert change['destination'] == ['105', 'Subpart', 'A'] assert change['action'] == 'DESIGNATE'
def process_xml(notice, notice_xml): """Pull out relevant fields from the xml and add them to the notice""" notice = dict(notice) # defensive copy if not notice.get('effective_on'): dates = fetch_dates(notice_xml) if dates and 'effective' in dates: notice['effective_on'] = dates['effective'][0] if not notice.get('cfr_parts'): cfr_parts = fetch_cfr_parts(notice_xml) notice['cfr_parts'] = cfr_parts process_sxs(notice, notice_xml) amds = fetch_amendments(notice_xml) if amds: notice['amendments'] = amds add_footnotes(notice, notice_xml) return notice
def test_process_amendments_section(): amdpar = "3. In § 105.1, revise paragraph (b) to read as follows:" with XMLBuilder("REGTEXT", PART="105", TITLE="12") as ctx: ctx.AMDPAR(amdpar) with ctx.SECTION(): ctx.SECTNO("§ 105.1") ctx.SUBJECT("Purpose.") ctx.STARS() ctx.P("(b) This part carries out.") preprocess_amdpars(ctx.xml) amendment = fetch.fetch_amendments(ctx.xml)[0] changes = dict(amendment['changes']) assert amendment['instruction'] == amdpar assert amendment['cfr_part'] == '105' assert ['105-1-b'] == list(changes.keys()) changes = changes['105-1-b'][0] assert changes['action'] == 'PUT' assert changes['node']['text'] == '(b) This part carries out.'