def process_xml(notice, notice_xml): """Pull out relevant fields from the xml and add them to the notice""" xml_chunk = notice_xml.xpath('//FURINF/P') if xml_chunk: notice['contact'] = xml_chunk[0].text addresses = fetch_addresses(notice_xml) if addresses: notice['addresses'] = addresses sxs = find_section_by_section(notice_xml) sxs = build_section_by_section(sxs, notice['cfr_part'], notice['meta']['start_page']) notice['section_by_section'] = sxs context = [] amends = [] for par in notice_xml.xpath('//AMDPAR'): amend_set, context = parse_amdpar(par, context) amends.extend(amend_set) if amends: notice['amendments'] = amends return notice
def test_parse_amdpar_interp_heading(self): text = "ii. The heading for 35(b) blah blah is revised." xml = etree.fromstring(u"<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(1, len(amends)) self.assertEqual("PUT", amends[0].action) self.assertEqual("[title]", amends[0].field) self.assertEqual(["1111", "35", "b", "Interp"], amends[0].label)
def test_parse_amdpar_interp_heading(self): text = "ii. The heading for 35(b) blah blah is revised." xml = etree.fromstring(u'<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(1, len(amends)) self.assertEqual('PUT', amends[0].action) self.assertEqual('[title]', amends[0].field) self.assertEqual(['1111', '35', 'b', 'Interp'], amends[0].label)
def test_parse_amdpar_add_field(self): text = "Adding introductory text to paragraph (c)" xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', None, '12']) self.assertEqual(1, len(amends)) amd = amends[0] self.assertEqual(amd.action, tokens.Verb.PUT) self.assertEqual(amd.label, ['1111', '12', 'c']) self.assertEqual(amd.field, '[text]')
def test_parse_amdpar_add_field(self): text = "Adding introductory text to paragraph (c)" xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", None, "12"]) self.assertEqual(1, len(amends)) amd = amends[0] self.assertEqual(amd.action, tokens.Verb.PUT) self.assertEqual(amd.label, ["1111", "12", "c"]) self.assertEqual(amd.field, "[text]")
def test_parse_amdpar_and_in_tags(self): text = "Under <E>Appendix A - Some phrase and another</E>, paragraph " text += "3 is added" xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(1, len(amends)) amend = amends[0] self.assertEqual("POST", amend.action) self.assertEqual(["1111", "A", "Interp", "3"], amend.label)
def process_amendments(notice, notice_xml): """ Process the changes to the regulation that are expressed in the notice. """ amends = [] notice_changes = changes.NoticeChanges() amdpars_by_parent = [] for par in notice_xml.xpath('//AMDPAR'): parent = par.getparent() exists = filter(lambda aXp: aXp.parent == parent, amdpars_by_parent) if exists: exists[0].append(par) else: amdpars_by_parent.append(AmdparByParent(parent, par)) for aXp in amdpars_by_parent: amended_labels = [] designate_labels, other_labels = [], [] context = [aXp.parent.get('PART') or notice['cfr_part']] for par in aXp.amdpars: als, context = parse_amdpar(par, context) amended_labels.extend(als) for al in amended_labels: if isinstance(al, DesignateAmendment): subpart_changes = process_designate_subpart(al) if subpart_changes: notice_changes.update(subpart_changes) designate_labels.append(al) elif new_subpart_added(al, notice['cfr_part']): notice_changes.update(process_new_subpart(notice, al, par)) designate_labels.append(al) else: other_labels.append(al) create_xmlless_changes(other_labels, notice_changes) section_xml = find_section(par) if section_xml is not None: for section in reg_text.build_from_section( notice['cfr_part'], section_xml): create_xml_changes(other_labels, section, notice_changes) for appendix in parse_appendix_changes(other_labels, notice['cfr_part'], aXp.parent): create_xml_changes(other_labels, appendix, notice_changes) interp = parse_interp_changes(other_labels, notice['cfr_part'], aXp.parent) if interp: create_xml_changes(other_labels, interp, notice_changes) amends.extend(designate_labels) amends.extend(other_labels) if amends: notice['amendments'] = amends notice['changes'] = notice_changes.changes
def test_parse_amdpar_and_in_tags(self): text = "Under <E>Appendix A - Some phrase and another</E>, paragraph " text += "3 is added" xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(1, len(amends)) amend = amends[0] self.assertEqual('POST', amend.action) self.assertEqual(['1111', 'A', 'Interp', '3'], amend.label)
def test_parse_amdpar_interp_entries(self): text = "Entries for 12(c)(3)(ix)(A) and (B) are added." xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(2, len(amends)) a, b = amends self.assertEqual("POST", a.action) self.assertEqual(["1111", "12", "c", "3", "ix", "A", "Interp"], a.label) self.assertEqual("POST", b.action) self.assertEqual(["1111", "12", "c", "3", "ix", "B", "Interp"], b.label)
def test_parse_amdpar_subject_group(self): xml = etree.fromstring( '<AMDPAR>8. Section 479.90a is added to ' '[subject-group(Exemptions Relating to Transfers of Firearms)] ' 'to read as follows.</AMDPAR>') amends, _ = diff.parse_amdpar(xml, []) self.assertEqual(1, len(amends)) self.assertEqual(amends[0].action, tokens.Verb.POST) self.assertEqual(amends[0].label, ['479', '90a']) self.assertEqual(amends[0].original_label, '479-Subjgrp:ERtToF-90a')
def test_parse_amdpar_and_and(self): text = "12(a) 'Titles and Paragraphs' and paragraph 3 are added" xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(2, len(amends)) a, b = amends self.assertEqual("POST", a.action) self.assertEqual(["1111", "12", "a", "Interp"], a.label) self.assertEqual("POST", b.action) self.assertEqual(["1111", "12", "a", "Interp", "3"], b.label)
def test_parse_amdpar_and_and(self): text = "12(a) 'Titles and Paragraphs' and paragraph 3 are added" xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(2, len(amends)) a, b = amends self.assertEqual('POST', a.action) self.assertEqual(['1111', '12', 'a', 'Interp'], a.label) self.assertEqual('POST', b.action) self.assertEqual(['1111', '12', 'a', 'Interp', '3'], b.label)
def test_parse_amdpar_interp_redesignated(self): text = "Paragraph 1 under 51(b) is redesignated as paragraph 2 " text += "under subheading 51(b)(1) and revised" xml = etree.fromstring(u'<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(2, len(amends)) delete, add = amends self.assertEqual('DELETE', delete.action) self.assertEqual(['1111', '51', 'b', 'Interp', '1'], delete.label) self.assertEqual('POST', add.action) self.assertEqual(['1111', '51', 'b', '1', 'Interp', '2'], add.label)
def test_parse_amdpar_interp_phrase(self): text = u"In Supplement I to part 999, under" text += u'<E T="03">Section 999.3—Header,</E>' text += u"under" text += u'<E T="03">3(b) Subheader,</E>' text += u"new paragraph 1.iv is added:" xml = etree.fromstring(u"<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111"]) self.assertEqual(1, len(amends)) self.assertEqual("POST", amends[0].action) self.assertEqual(["999", "3", "b", "Interp", "1", "iv"], amends[0].label)
def test_parse_amdpar_interp_redesignated(self): text = "Paragraph 1 under 51(b) is redesignated as paragraph 2 " text += "under subheading 51(b)(1) and revised" xml = etree.fromstring(u"<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(2, len(amends)) delete, add = amends self.assertEqual("DELETE", delete.action) self.assertEqual(["1111", "51", "b", "Interp", "1"], delete.label) self.assertEqual("POST", add.action) self.assertEqual(["1111", "51", "b", "1", "Interp", "2"], add.label)
def test_parse_amdpar_interp_entries(self): text = "Entries for 12(c)(3)(ix)(A) and (B) are added." xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(2, len(amends)) a, b = amends self.assertEqual('POST', a.action) self.assertEqual(['1111', '12', 'c', '3', 'ix', 'A', 'Interp'], a.label) self.assertEqual('POST', b.action) self.assertEqual(['1111', '12', 'c', '3', 'ix', 'B', 'Interp'], b.label)
def test_parse_amdpar_interp_context(self): text = "b. 35(b)(1) Some title and paragraphs 1, 2, and 3 are added." xml = etree.fromstring(u"<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(4, len(amends)) for amd in amends: self.assertEqual("POST", amd.action) amd35b1, amd35b1_1, amd35b1_2, amd35b1_3 = amends self.assertEqual(["1111", "35", "b", "1", "Interp"], amd35b1.label) self.assertEqual(["1111", "35", "b", "1", "Interp", "1"], amd35b1_1.label) self.assertEqual(["1111", "35", "b", "1", "Interp", "2"], amd35b1_2.label) self.assertEqual(["1111", "35", "b", "1", "Interp", "3"], amd35b1_3.label)
def test_parse_amdpar_interp_phrase(self): text = u"In Supplement I to part 999, under" text += u'<E T="03">Section 999.3—Header,</E>' text += u"under" text += u'<E T="03">3(b) Subheader,</E>' text += u"new paragraph 1.iv is added:" xml = etree.fromstring(u'<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111']) self.assertEqual(1, len(amends)) self.assertEqual('POST', amends[0].action) self.assertEqual(['999', '3', 'b', 'Interp', '1', 'iv'], amends[0].label)
def test_parse_amdpar_newly_redesignated(self): text = "Paragraphs 3.ii, 3.iii, 4 and newly redesignated paragraph " text += "10 are revised." xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations", "2", "(a)"]) self.assertEqual(4, len(amends)) self.assertEqual(["1111", "2", "a", "Interp", "3", "ii"], amends[0].label) self.assertEqual(["1111", "2", "a", "Interp", "3", "iii"], amends[1].label) self.assertEqual(["1111", "2", "a", "Interp", "4"], amends[2].label) self.assertEqual(["1111", "2", "a", "Interp", "10"], amends[3].label) for amend in amends: self.assertEqual(amend.action, "PUT")
def test_parse_amdpar_definition(self): """We should correctly deduce which paragraphs are being updated, even when they are identified by definition alone""" text = ("Section 478.11 is amended by adding a definition for the " u"term “Nonimmigrant visa” in alphabetical order to read as " "follows:") xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, []) self.assertEqual(1, len(amends)) self.assertEqual(amends[0].action, tokens.Verb.POST) self.assertEqual(3, len(amends[0].label)) self.assertEqual(['478', '11'], amends[0].label[:2]) # Paragraph has a hash self.assertTrue(re.match(r'p\d{4}\d+', amends[0].label[2]))
def process_amendments(notice, notice_xml): """Process changes to the regulation that are expressed in the notice.""" all_amends = [] # will be added to the notice cfr_part = notice['cfr_parts'][0] notice_changes = changes.NoticeChanges() # process amendments in batches, based on their parent XML for amdparent in notice_xml.xpath('//AMDPAR/..'): context = [amdparent.get('PART') or cfr_part] amendments_by_section = defaultdict(list) normal_amends = [] # amendments not moving or adding a subpart for amdpar in amdparent.xpath('.//AMDPAR'): amendments, context = parse_amdpar(amdpar, context) section_xml = find_section(amdpar) for amendment in amendments: all_amends.append(amendment) if isinstance(amendment, DesignateAmendment): subpart_changes = process_designate_subpart(amendment) if subpart_changes: notice_changes.update(subpart_changes) elif new_subpart_added(amendment): notice_changes.update(process_new_subpart( notice, amendment, amdpar)) elif section_xml is None: normal_amends.append(amendment) else: normal_amends.append(amendment) amendments_by_section[section_xml].append(amendment) cfr_part = context[0] # carry the part through to the next amdparent create_xmlless_changes(normal_amends, notice_changes) # Process amendments relating to a specific section in batches, too for section_xml, related_amends in amendments_by_section.items(): for section in reg_text.build_from_section(cfr_part, section_xml): create_xml_changes(related_amends, section, notice_changes) for appendix in parse_appendix_changes(normal_amends, cfr_part, amdparent): create_xml_changes(normal_amends, appendix, notice_changes) interp = parse_interp_changes(normal_amends, cfr_part, amdparent) if interp: create_xml_changes(normal_amends, interp, notice_changes) if all_amends: notice['amendments'] = all_amends notice['changes'] = notice_changes.changes return notice
def test_parse_amdpar_newly_redesignated(self): text = "Paragraphs 3.ii, 3.iii, 4 and newly redesignated paragraph " text += "10 are revised." xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations', '2', '(a)']) self.assertEqual(4, len(amends)) self.assertEqual(['1111', '2', 'a', 'Interp', '3', 'ii'], amends[0].label) self.assertEqual(['1111', '2', 'a', 'Interp', '3', 'iii'], amends[1].label) self.assertEqual(['1111', '2', 'a', 'Interp', '4'], amends[2].label) self.assertEqual(['1111', '2', 'a', 'Interp', '10'], amends[3].label) for amend in amends: self.assertEqual(amend.action, 'PUT')
def test_parse_amdpar_interp_context(self): text = "b. 35(b)(1) Some title and paragraphs 1, 2, and 3 are added." xml = etree.fromstring(u'<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(4, len(amends)) for amd in amends: self.assertEqual('POST', amd.action) amd35b1, amd35b1_1, amd35b1_2, amd35b1_3 = amends self.assertEqual(['1111', '35', 'b', '1', 'Interp'], amd35b1.label) self.assertEqual(['1111', '35', 'b', '1', 'Interp', '1'], amd35b1_1.label) self.assertEqual(['1111', '35', 'b', '1', 'Interp', '2'], amd35b1_2.label) self.assertEqual(['1111', '35', 'b', '1', 'Interp', '3'], amd35b1_3.label)
def test_parse_amdpar_moved_then_modified(self): text = "Under Paragraph 22(a), paragraph 1 is revised, paragraph " text += "2 is redesignated as paragraph 3 and revised, and new " text += "paragraph 2 is added." xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(4, len(amends)) a1, a2del, a3, a2add = amends self.assertEqual(a1.action, tokens.Verb.PUT) self.assertEqual(a1.label, ["1111", "22", "a", "Interp", "1"]) self.assertEqual(a2del.action, tokens.Verb.DELETE) self.assertEqual(a2del.label, ["1111", "22", "a", "Interp", "2"]) self.assertEqual(a3.action, tokens.Verb.POST) self.assertEqual(a3.label, ["1111", "22", "a", "Interp", "3"]) self.assertEqual(a2add.action, tokens.Verb.POST) self.assertEqual(a2add.label, ["1111", "22", "a", "Interp", "2"])
def test_parse_amdpar_moved_then_modified(self): text = "Under Paragraph 22(a), paragraph 1 is revised, paragraph " text += "2 is redesignated as paragraph 3 and revised, and new " text += "paragraph 2 is added." xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(4, len(amends)) a1, a2del, a3, a2add = amends self.assertEqual(a1.action, tokens.Verb.PUT) self.assertEqual(a1.label, ['1111', '22', 'a', 'Interp', '1']) self.assertEqual(a2del.action, tokens.Verb.DELETE) self.assertEqual(a2del.label, ['1111', '22', 'a', 'Interp', '2']) self.assertEqual(a3.action, tokens.Verb.POST) self.assertEqual(a3.label, ['1111', '22', 'a', 'Interp', '3']) self.assertEqual(a2add.action, tokens.Verb.POST) self.assertEqual(a2add.label, ['1111', '22', 'a', 'Interp', '2'])
def test_parse_amdpar_verbs_ands(self): text = "Under 45(a)(1) Title, paragraphs 1 and 2 are removed, and " text += "45(a)(1)(i) Deeper Title and paragraphs 1 and 2 are added" xml = etree.fromstring("<AMDPAR>%s</AMDPAR>" % text) amends, _ = diff.parse_amdpar(xml, ["1111", "Interpretations"]) self.assertEqual(5, len(amends)) a11, a12, a1i, a1i1, a1i2 = amends self.assertEqual("DELETE", a11.action) self.assertEqual(["1111", "45", "a", "1", "Interp", "1"], a11.label) self.assertEqual("DELETE", a12.action) self.assertEqual(["1111", "45", "a", "1", "Interp", "2"], a12.label) self.assertEqual("POST", a1i.action) self.assertEqual(["1111", "45", "a", "1", "i", "Interp"], a1i.label) self.assertEqual("POST", a1i1.action) self.assertEqual(["1111", "45", "a", "1", "i", "Interp", "1"], a1i1.label) self.assertEqual("POST", a1i2.action) self.assertEqual(["1111", "45", "a", "1", "i", "Interp", "2"], a1i2.label)
def test_parse_amdpar_verbs_ands(self): text = "Under 45(a)(1) Title, paragraphs 1 and 2 are removed, and " text += "45(a)(1)(i) Deeper Title and paragraphs 1 and 2 are added" xml = etree.fromstring('<AMDPAR>%s</AMDPAR>' % text) amends, _ = diff.parse_amdpar(xml, ['1111', 'Interpretations']) self.assertEqual(5, len(amends)) a11, a12, a1i, a1i1, a1i2 = amends self.assertEqual('DELETE', a11.action) self.assertEqual(['1111', '45', 'a', '1', 'Interp', '1'], a11.label) self.assertEqual('DELETE', a12.action) self.assertEqual(['1111', '45', 'a', '1', 'Interp', '2'], a12.label) self.assertEqual('POST', a1i.action) self.assertEqual(['1111', '45', 'a', '1', 'i', 'Interp'], a1i.label) self.assertEqual('POST', a1i1.action) self.assertEqual(['1111', '45', 'a', '1', 'i', 'Interp', '1'], a1i1.label) self.assertEqual('POST', a1i2.action) self.assertEqual(['1111', '45', 'a', '1', 'i', 'Interp', '2'], a1i2.label)
def process_amendments(notice, notice_xml): """ Process the changes to the regulation that are expressed in the notice. """ amends = [] notice_changes = changes.NoticeChanges() amdpars_by_parent = [] for par in notice_xml.xpath('//AMDPAR'): parent = par.getparent() exists = filter(lambda aXp: aXp.parent == parent, amdpars_by_parent) if exists: exists[0].append(par) else: amdpars_by_parent.append(AmdparByParent(parent, par)) default_cfr_part = notice['cfr_parts'][0] for aXp in amdpars_by_parent: amended_labels = [] designate_labels, other_labels = [], [] context = [aXp.parent.get('PART') or default_cfr_part] for par in aXp.amdpars: als, context = parse_amdpar(par, context) amended_labels.extend(als) labels_by_part = defaultdict(list) for al in amended_labels: if isinstance(al, DesignateAmendment): subpart_changes = process_designate_subpart(al) if subpart_changes: notice_changes.update(subpart_changes) designate_labels.append(al) elif new_subpart_added(al): notice_changes.update(process_new_subpart(notice, al, par)) designate_labels.append(al) else: other_labels.append(al) labels_by_part[al.label[0]].append(al) create_xmlless_changes(other_labels, notice_changes) for cfr_part, rel_labels in labels_by_part.iteritems(): section_xml = find_section(par) if section_xml is not None: for section in reg_text.build_from_section( cfr_part, section_xml): create_xml_changes(rel_labels, section, notice_changes) for appendix in parse_appendix_changes(rel_labels, cfr_part, aXp.parent): create_xml_changes(rel_labels, appendix, notice_changes) interp = parse_interp_changes(rel_labels, cfr_part, aXp.parent) if interp: create_xml_changes(rel_labels, interp, notice_changes) amends.extend(designate_labels) amends.extend(other_labels) if other_labels: # Carry cfr_part through amendments default_cfr_part = other_labels[-1].label[0] if amends: notice['amendments'] = amends notice['changes'] = notice_changes.changes
def process_amendments(notice, notice_xml): """ Process the changes to the regulation that are expressed in the notice. """ amends = [] notice_changes = changes.NoticeChanges() amdpars_by_parent = [] for par in notice_xml.xpath('//AMDPAR'): parent = par.getparent() exists = filter(lambda aXp: aXp.parent == parent, amdpars_by_parent) if exists: exists[0].append(par) else: amdpars_by_parent.append(AmdparByParent(parent, par)) default_cfr_part = notice['cfr_part'] for aXp in amdpars_by_parent: amended_labels = [] designate_labels, other_labels = [], [] context = [default_cfr_part] for par in aXp.amdpars: als, context = parse_amdpar(par, context) amended_labels.extend(als) labels_by_part = defaultdict(list) for al in amended_labels: if isinstance(al, DesignateAmendment): subpart_changes = process_designate_subpart(al) if subpart_changes: notice_changes.update(subpart_changes) designate_labels.append(al) elif new_subpart_added(al): notice_changes.update(process_new_subpart(notice, al, par)) designate_labels.append(al) else: other_labels.append(al) labels_by_part[al.label[0]].append(al) create_xmlless_changes(other_labels, notice_changes) # for cfr_part, rel_labels in labels_by_part.iteritems(): labels_for_part = { part: labels for part, labels in labels_by_part.iteritems() if part == default_cfr_part } print(labels_for_part) for cfr_part, rel_labels in labels_for_part.iteritems(): section_xml = find_section(par) if section_xml is not None: subparts = aXp.parent.xpath('.//SUBPART/HD') if subparts: subpart_label = [ cfr_part, 'Subpart', subparts[0].text[8:9] ] else: subpart_label = None for section in reg_text.build_from_section( cfr_part, section_xml): create_xml_changes(rel_labels, section, notice_changes, subpart_label) for appendix in parse_appendix_changes(rel_labels, cfr_part, aXp.parent): create_xml_changes(rel_labels, appendix, notice_changes) interp = parse_interp_changes(rel_labels, cfr_part, aXp.parent) if interp: create_xml_changes(rel_labels, interp, notice_changes) amends.extend(designate_labels) amends.extend(other_labels) # if other_labels: # Carry cfr_part through amendments # default_cfr_part = other_labels[-1].label[0] if amends: notice['amendments'] = amends notice['changes'] = notice_changes.changes elif notice['document_number'] in settings.REISSUANCES: notice['changes'] = { default_cfr_part: [{ 'action': 'PUT', 'node': reg_text.build_tree(notice_xml) }] }
def process_amendments(notice, notice_xml): """ Process the changes to the regulation that are expressed in the notice. """ amends = [] notice_changes = changes.NoticeChanges() amdpars_by_parent = [] for par in notice_xml.xpath('//AMDPAR'): parent = par.getparent() exists = filter(lambda aXp: aXp.parent == parent, amdpars_by_parent) if exists: exists[0].append(par) else: amdpars_by_parent.append(AmdparByParent(parent, par)) default_cfr_part = notice['cfr_parts'][0] for aXp in amdpars_by_parent: amended_labels = [] designate_labels, other_labels = [], [] context = [aXp.parent.get('PART') or default_cfr_part] for par in aXp.amdpars: als, context = parse_amdpar(par, context) amended_labels.extend(als) labels_by_part = defaultdict(list) for al in amended_labels: if isinstance(al, DesignateAmendment): subpart_changes = process_designate_subpart(al) if subpart_changes: notice_changes.update(subpart_changes) designate_labels.append(al) elif new_subpart_added(al): notice_changes.update(process_new_subpart(notice, al, par)) designate_labels.append(al) else: other_labels.append(al) labels_by_part[al.label[0]].append(al) create_xmlless_changes(other_labels, notice_changes) for cfr_part, rel_labels in labels_by_part.iteritems(): section_xml = find_section(par) if section_xml is not None: subparts = aXp.parent.xpath('.//SUBPART/HD') if subparts: subpart_label = [cfr_part, 'Subpart', subparts[0].text[8:9]] else: subpart_label = None for section in reg_text.build_from_section(cfr_part, section_xml): create_xml_changes(rel_labels, section, notice_changes, subpart_label) for appendix in parse_appendix_changes(rel_labels, cfr_part, aXp.parent): create_xml_changes(rel_labels, appendix, notice_changes) interp = parse_interp_changes(rel_labels, cfr_part, aXp.parent) if interp: create_xml_changes(rel_labels, interp, notice_changes) amends.extend(designate_labels) amends.extend(other_labels) if other_labels: # Carry cfr_part through amendments default_cfr_part = other_labels[-1].label[0] if amends: notice['amendments'] = amends notice['changes'] = notice_changes.changes elif notice['document_number'] in settings.REISSUANCES: notice['changes'] = { default_cfr_part: [{ 'action': 'PUT', 'node': reg_text.build_tree(notice_xml) }] }