def test_walk(self): actual_data = [ (root[len(self.root):], account_, dirs, files) for root, account_, dirs, files in account.walk(self.root)] self.assertEqual([ ('/Assets/US', 'Assets:US', ['Bank'], []), ('/Assets/US/Bank', 'Assets:US:Bank', ['Checking', 'Savings'], []), ('/Assets/US/Bank/Checking', 'Assets:US:Bank:Checking', ['otherdir'], ['2014-06-08.bank-statement.pdf', 'other.txt']), ('/Assets/US/Bank/Savings', 'Assets:US:Bank:Savings', [], ['2014-07-01.savings.pdf']), ('/Liabilities/US', 'Liabilities:US', ['Bank'], []), ('/Liabilities/US/Bank', 'Liabilities:US:Bank', [], []), ], actual_data)
def validate_directory(accounts, document_dir): """Check a directory hierarchy against a list of valid accounts. Walk the directory hierarchy, and for all directories with names matching that of accounts (with ":" replaced with "/"), check that they refer to an account name declared in the given list. Args: account: A set or dict of account names. document_dir: A string, the root directory to walk and validate. Returns: An errors for each invalid directory name found. """ # Generate all parent accounts in the account_set we're checking against, so # that parent directories with no corresponding account don't warn. accounts_with_parents = set(accounts) for account_ in accounts: while True: parent = account.parent(account_) if not parent: break if parent in accounts_with_parents: break accounts_with_parents.add(parent) account_ = parent errors = [] for directory, account_name, _, _ in account.walk(document_dir): if account_name not in accounts_with_parents: errors.append( ValidateDirectoryError( "Invalid directory '{}': no corresponding account '{}'". format(directory, account_name))) return errors
def find_documents(directory, input_filename, accounts_only=None, strict=False): """Find dated document files under the given directory. If a restricting set of accounts is provided in 'accounts_only', only return entries that correspond to one of the given accounts. Args: directory: A string, the name of the root of the directory hierarchy to be searched. input_filename: The name of the file to be used for the Document directives. This is also used to resolve relative directory names. accounts_only: A set of valid accounts strings to search for. strict: A boolean, set to true if you want to generate errors on documents found in accounts not provided in accounts_only. This is only meaningful if accounts_only is specified. Returns: A list of new Document objects that were created from the files found, and a list of new errors generated. """ errors = [] # Compute the documents directory name relative to the beancount input # file itself. if not path.isabs(directory): input_directory = path.dirname(input_filename) directory = path.abspath( path.normpath(path.join(input_directory, directory))) # If the directory does not exist, just generate an error and return. if not path.exists(directory): meta = data.new_metadata(input_filename, 0) error = DocumentError( meta, "Document root '{}' does not exist".format(directory), None) return ([], [error]) # Walk the hierarchy of files. entries = [] for root, account_name, dirs, files in account.walk(directory): # Look for files that have a dated filename. for filename in files: match = re.match('(\d\d\d\d)-(\d\d)-(\d\d).(.*)', filename) if not match: continue # If a restricting set of accounts was specified, skip document # directives found in accounts with no corresponding account name. if accounts_only and not account_name in accounts_only: if strict: if any( account_name.startswith(account) for account in accounts_only): errors.append( DocumentError( data.new_metadata(input_filename, 0), "Document '{}' found in child account {}". format(filename, account_name), None)) elif any( account.startswith(account_name) for account in accounts_only): errors.append( DocumentError( data.new_metadata(input_filename, 0), "Document '{}' found in parent account {}". format(filename, account_name), None)) continue # Create a new directive. meta = data.new_metadata(input_filename, 0) date = datetime.date(*map(int, match.group(1, 2, 3))) entry = Document(meta, date, account_name, path.join(root, filename)) entries.append(entry) return (entries, errors)