def test_derived_from_rules(self): """Should filter a set of version ids to only those with a dependency on changes derived from a rule""" with self.cli.isolated_filesystem(): tree_dir = entry.Tree('12', '1000') deps = dependency.Graph() deps.add(tree_dir / 111, entry.Annual(12, 1000, 2001)) deps.add(tree_dir / 222, entry.RuleChanges(222)) deps.add(tree_dir / 333, entry.RuleChanges(333)) deps.add(tree_dir / 333, entry.Version(333)) derived = fill_with_rules.derived_from_rules( ['111', '222', '333', '444'], deps, tree_dir) self.assertEqual(derived, ['222', '333'])
def process(tree_path, previous, version_id): """Build and write a tree by combining the preceding tree with changes present in the associated rule""" prev_tree = (tree_path / previous).read() notice = entry.RuleChanges(version_id).read() changes = merge_changes(version_id, notice.get('changes', {})) new_tree = compile_regulation(prev_tree, changes) (tree_path / version_id).write(new_tree)
def derived_from_rules(version_ids, deps, tree_path): """We only want to process trees which are created by parsing rules. To do that, we'll filter by those trees which have a dependency on a parsed rule""" rule_versions = [] for version_id in version_ids: path = str(tree_path / version_id) rule_change = str(entry.RuleChanges(version_id)) if rule_change in deps.dependencies(path): rule_versions.append(version_id) return rule_versions
def dependencies(tree_path, version_ids, cfr_title, cfr_part): """Set up the dependency graph for this regulation. First calculates "gaps" -- versions for which there is no existing tree. In this calculation, we ignore the first version, as we won't be able to build anything for it. Add dependencies for any gaps, tying the output tree to the preceding tree, the version info and the parsed rule""" existing_ids = set(tree_path) gaps = [(prev, curr) for prev, curr in zip(version_ids, version_ids[1:]) if curr not in existing_ids] deps = dependency.Graph() for prev, curr in gaps: deps.add(tree_path / curr, tree_path / prev) deps.add(tree_path / curr, entry.RuleChanges(curr)) deps.add(tree_path / curr, entry.Version(cfr_title, cfr_part, curr)) return deps
def parse_rule_changes(document_number): """Parse changes present in a single rule. DOCUMENT_NUMBER is the identifier associated with a final rule. If a rule has been split, use the split identifiers, a.k.a. version ids.""" rule_entry = entry.RuleChanges(document_number) notice_entry = entry.Notice(document_number) deps = dependency.Graph() deps.add(rule_entry, notice_entry) deps.validate_for(rule_entry) # We don't check for staleness as we want to always execute when given a # specific file to process notice_xml = notice_entry.read() notice = process_amendments({'cfr_parts': notice_xml.cfr_parts}, notice_xml.xml) rule_entry.write(notice)
def test_dependencies(self): """Expect nonexistent trees to depend on their predecessor, associated rule changes and version files. Shouldn't add dependencies for the first version, if missing""" with self.cli.isolated_filesystem(): version_ids = ['111', '222', '333', '444', '555', '666'] tree_dir = entry.Tree('12', '1000') rule_dir = entry.RuleChanges() vers_dir = entry.Version('12', '1000') # Existing trees (tree_dir / '222').write(Node()) (tree_dir / '555').write(Node()) deps = fill_with_rules.dependencies(tree_dir, version_ids, '12', '1000') # First is skipped, as we can't build it from a rule self.assertNotIn(str(tree_dir / '111'), deps) # Second can also be skipped as a tree already exists self.assertEqual(deps.dependencies(str(tree_dir / '222')), []) # Third relies on the associated versions and the second tree self.assertItemsEqual(deps.dependencies(str(tree_dir / '333')), [ str(tree_dir / '222'), str(rule_dir / '333'), str(vers_dir / '333') ]) # Fourth relies on the third, even though it's not been built self.assertItemsEqual(deps.dependencies(str(tree_dir / '444')), [ str(tree_dir / '333'), str(rule_dir / '444'), str(vers_dir / '444') ]) # Fifth can be skipped as the tree already exists self.assertEqual(deps.dependencies(str(tree_dir / '555')), []) # Six relies on the fifth self.assertItemsEqual(deps.dependencies(str(tree_dir / '666')), [ str(tree_dir / '555'), str(rule_dir / '666'), str(vers_dir / '666') ])