def test_multiple_changes(self):
        """ A notice can have two modifications to a paragraph. """
        amdpar1 = (u"2. Designate §§ 106.1 through 106.3 as subpart A under "
                   u"the heading.")
        amdpar2 = u"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(u"§ 106.2")
                    ctx.SUBJECT(" Definitions ")
                    ctx.P(" Except as otherwise provided, the following "
                          "apply. ")
        ParseAMDPARs().transform(ctx.xml)

        amd1, amd2 = amendments.fetch_amendments(ctx.xml)
        changes1, changes2 = dict(amd1['changes']), dict(amd2['changes'])
        self.assertEqual(amd1['instruction'], amdpar1)
        self.assertEqual(amd1['cfr_part'], '106')
        self.assertEqual(amd2['instruction'], amdpar2)
        self.assertEqual(amd2['cfr_part'], '106')
        self.assertEqual(1, len(changes1['106-2']))
        self.assertEqual(1, len(changes2['106-2']))
    def test_process_amendments_subpart(self):
        with XMLBuilder("RULE") as ctx:
            with ctx.REGTEXT(PART="105", TITLE="12"):
                ctx.AMDPAR(u"3. In § 105.1, revise paragraph (b) to read as"
                           u"follows:")
                with ctx.SECTION():
                    ctx.SECTNO(u"§ 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(u"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.')

        ParseAMDPARs().transform(ctx.xml)

        subpart_amendment = amendments.fetch_amendments(ctx.xml)[1]
        changes = dict(subpart_amendment['changes'])

        self.assertTrue('105-Subpart-B' in changes)
        self.assertTrue('105-30-a' in changes)
        self.assertTrue('105-30' in changes)
    def test_process_amendments_mix_regs(self):
        """Some notices apply to multiple regs. For now, just ignore the
        sections not associated with the reg we're focused on"""
        amdpar1 = u"3. In § 105.1, revise paragraph (a) to read as follows:"
        amdpar2 = u"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(u"§ 105.1")
                    ctx.SUBJECT("105Purpose.")
                    ctx.P("(a) 105Content")
            with ctx.REGTEXT(PART="106", TITLE="12"):
                ctx.AMDPAR(amdpar2)
                with ctx.SECTION():
                    ctx.SECTNO(u"§ 106.3")
                    ctx.SUBJECT("106Purpose.")
                    ctx.P("(b) Content")
        ParseAMDPARs().transform(ctx.xml)

        amd1, amd2 = amendments.fetch_amendments(ctx.xml)
        self.assertEqual(amd1['instruction'], amdpar1)
        self.assertEqual(amd1['cfr_part'], '105')
        self.assertEqual(amd2['instruction'], amdpar2)
        self.assertEqual(amd2['cfr_part'], '106')
        six.assertCountEqual(self,
                             [c[0] for c in amd1['changes']], ['105-1-a'])
        six.assertCountEqual(self,
                             [c[0] for c in amd2['changes']], ['106-3-b'])
    def test_process_amendments_restart_new_section(self):
        amdpar1 = "1. In Supplement I to Part 104, comment 22(a) is added"
        amdpar2 = u"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(u"§ 105.1")
                    ctx.SUBJECT("Purpose.")
                    ctx.STARS()
                    ctx.P("(b) This part carries out.")
        ParseAMDPARs().transform(ctx.xml)

        amd1, amd2 = amendments.fetch_amendments(ctx.xml)
        changes1, changes2 = dict(amd1['changes']), dict(amd2['changes'])
        self.assertEqual(amd1['instruction'], amdpar1)
        self.assertEqual(amd1['cfr_part'], '104')
        self.assertEqual(amd2['instruction'], amdpar2)
        self.assertEqual(amd2['cfr_part'], '105')
        self.assertIn('104-22-a-Interp', changes1)
        self.assertIn('105-1-b', changes2)

        self.assertEqual(changes1['104-22-a-Interp'][0]['action'], 'POST')
        self.assertEqual(changes2['105-1-b'][0]['action'], 'PUT')
    def test_process_amendments_multiple_sections(self):
        """Regression test verifying multiple SECTIONs in the same REGTEXT"""
        amdpar1 = u"1. Modify § 111.22 by revising paragraph (b)"
        amdpar2 = u"2. Modify § 111.33 by revising paragraph (c)"
        with XMLBuilder("REGTEXT", PART="111") as ctx:
            ctx.AMDPAR(amdpar1)
            with ctx.SECTION():
                ctx.SECTNO(u"§ 111.22")
                ctx.SUBJECT("Subject Here.")
                ctx.STARS()
                ctx.P("(b) Revised second paragraph")
            ctx.AMDPAR(amdpar2)
            with ctx.SECTION():
                ctx.SECTNO(u"§ 111.33")
                ctx.SUBJECT("Another Subject")
                ctx.STARS()
                ctx.P("(c) Revised third paragraph")
        ParseAMDPARs().transform(ctx.xml)

        amd1, amd2 = amendments.fetch_amendments(ctx.xml)
        self.assertEqual(amd1['instruction'], amdpar1)
        self.assertEqual(amd1['cfr_part'], '111')
        six.assertCountEqual(self,
                             [c[0] for c in amd1['changes']], ['111-22-b'])
        self.assertEqual(amd2['instruction'], amdpar2)
        self.assertEqual(amd2['cfr_part'], '111')
        six.assertCountEqual(self,
                             [c[0] for c in amd2['changes']], ['111-33-c'])
    def test_process_amendments_multiple_in_same_parent(self):
        amdpar1 = u"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(u"§ 105.1")
                ctx.SUBJECT("Purpose.")
                ctx.STARS()
                ctx.P("(b) This part carries out.")
                ctx.P("(c) More stuff")
        ParseAMDPARs().transform(ctx.xml)

        amd1, amd2 = amendments.fetch_amendments(ctx.xml)
        changes1, changes2 = dict(amd1['changes']), dict(amd2['changes'])
        self.assertEqual(amd1['instruction'], amdpar1)
        self.assertEqual(amd1['cfr_part'], '105')
        self.assertEqual(amd2['instruction'], amdpar2)
        self.assertEqual(amd2['cfr_part'], '105')
        six.assertCountEqual(self, changes1.keys(), ['105-1-b'])
        six.assertCountEqual(self, changes2.keys(), ['105-1-c'])

        changes = changes1['105-1-b'][0]
        self.assertEqual(changes['action'], 'PUT')
        self.assertEqual(changes['node']['text'].strip(),
                         u'(b) This part carries out.')
        changes = changes2['105-1-c'][0]
        self.assertEqual(changes['action'], 'PUT')
        self.assertTrue(changes['node']['text'].strip(),
                        u'(c) More stuff')
    def test_process_amendments_context(self):
        """Context should carry over between REGTEXTs"""
        amdpar1 = u"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(u"§ 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")
        ParseAMDPARs().transform(ctx.xml)

        amd1, amd2 = amendments.fetch_amendments(ctx.xml)
        self.assertEqual(amd1['instruction'], amdpar1)
        self.assertEqual(amd1['cfr_part'], '106')
        self.assertEqual(amd2['instruction'], amdpar2)
        self.assertEqual(amd2['cfr_part'], '106')
        six.assertCountEqual(self,
                             [c[0] for c in amd1['changes']], ['106-1-a'])
        six.assertCountEqual(
            self,
            [c[0] for c in amd2['changes']], ['106-C', '106-C-p1'])
    def test_process_amendments_no_nodes(self):
        amdpar = u"1. In § 104.13, paragraph (b) is removed"
        with XMLBuilder("ROOT") as ctx:
            with ctx.REGTEXT(PART="104", TITLE="12"):
                ctx.AMDPAR(amdpar)
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        changes = dict(amendment['changes'])

        self.assertEqual(amendment['instruction'], amdpar)
        self.assertEqual(amendment['cfr_part'], '104')
        self.assertIn('104-13-b', changes)
        self.assertEqual(changes['104-13-b'][0]['action'], 'DELETE')
    def test_introductory_text(self):
        """ 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(u"3. In § 106.2, revise the introductory text to read:")
            with ctx.SECTION():
                ctx.SECTNO(u"§ 106.2")
                ctx.SUBJECT(" Definitions ")
                ctx.P(" Except as otherwise provided, the following apply. ")
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        change = dict(amendment['changes'])['106-2'][0]
        self.assertEqual('[text]', change.get('field'))
    def test_process_amendments_authority(self):
        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)
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        self.assertEqual(amendment['instruction'], amdpar)
        self.assertEqual(amendment['cfr_part'], '555')
        self.assertEqual(amendment['authority'], auth)
        self.assertNotIn('changes', amendment)
    def test_process_amendments_markerless(self):
        amdpar = u"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(u"§ 105.11")
                ctx.SUBJECT("Purpose.")
                ctx.STARS()
                ctx.P("Some text here")
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        changes = dict(amendment['changes'])

        self.assertEqual(amendment['instruction'], amdpar)
        self.assertEqual(amendment['cfr_part'], '105')
        six.assertCountEqual(self, changes.keys(), ['105-11-p5'])
        changes = changes['105-11-p5'][0]
        self.assertEqual(changes['action'], 'PUT')
    def test_process_amendments_insert_in_order(self):
        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(u"§ 123.45")
                    ctx.SUBJECT("Some Subject.")
                    ctx.STARS()
                    ctx.P("This is the sixth paragraph")
                    ctx.STARS()
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        changes = dict(amendment['changes'])

        self.assertEqual(amendment['instruction'], amdpar)
        self.assertEqual(amendment['cfr_part'], '123')
        six.assertCountEqual(self, ['123-45-p6'], changes.keys())
        self.assertEqual('INSERT', changes['123-45-p6'][0]['action'])
Example #13
0
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(self):
        amdpar = (u"2. Designate §§ 105.1 through 105.3 as subpart A under "
                  u"the heading.")
        with XMLBuilder("REGTEXT", PART="105", TITLE="12") as ctx:
            with ctx.SUBPART():
                ctx.HD(u"Subpart A—General", SOURCE="HED")
            ctx.AMDPAR(amdpar)
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        changes = dict(amendment['changes'])

        self.assertEqual(amendment['instruction'], amdpar)
        self.assertEqual(amendment['cfr_part'], '105')
        six.assertCountEqual(self, ['105-1', '105-2', '105-3'], changes.keys())
        for change_list in changes.values():
            self.assertEqual(1, len(change_list))
            change = change_list[0]
            self.assertEqual(change['destination'], ['105', 'Subpart', 'A'])
            self.assertEqual(change['action'], 'DESIGNATE')
Example #15
0
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(self):
        amdpar = u"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(u"§ 105.1")
                ctx.SUBJECT("Purpose.")
                ctx.STARS()
                ctx.P("(b) This part carries out.")
        ParseAMDPARs().transform(ctx.xml)

        amendment = amendments.fetch_amendments(ctx.xml)[0]
        changes = dict(amendment['changes'])

        self.assertEqual(amendment['instruction'], amdpar)
        self.assertEqual(amendment['cfr_part'], '105')
        six.assertCountEqual(self, changes.keys(), ['105-1-b'])

        changes = changes['105-1-b'][0]
        self.assertEqual(changes['action'], 'PUT')
        self.assertTrue(changes['node']['text'].startswith(
            u'(b) This part carries out.'))
Example #17
0
 def amendments(self):
     return fetch_amendments(self.xml)
Example #18
0
 def amendments(self):
     return fetch_amendments(self.xml)