#NEWLINE# def __init__( self , path , budget , device = None ) : #NEWLINE# #INDENT# '\n        Load YNAB budget from the specified path.\n\n        Parameters\n        ----------\n        path : str\n            Path to the budget root, e.g. ~/Documents/YNAB for local budgets or ~/Dropbox/YNAB\n            for cloud-synced budgets.\n        budget : str\n            Budget name.\n        device : str (optional)\n            Device name -- this corresponds to the .ydevice files in the "devices" folder.\n            Can be either A, B, C, ... or a full device name (hostname for desktops). The\n            full name can be found in .ydevice files in the "devices" folder. If this\n            parameter is not specified, the device with the latest modification time of\n            the budget file will be selected.\n        ' #NEWLINE# root = os . path . abspath ( os . path . expanduser ( path ) ) #NEWLINE# pattern = re . compile ( ( ( '^' + re . escape ( budget ) ) + '~[A-F0-9]{8}\\.ynab4$' ) ) #NEWLINE# folders = list ( filter ( pattern . match , os . listdir ( root ) ) ) #NEWLINE# if ( not folders ) : #NEWLINE# #INDENT# raise RuntimeError ( 'Budget {!r} not found at: {}' . format ( budget , path ) ) #NEWLINE# #UNINDENT# if ( len ( folders ) > 1 ) : #NEWLINE# #INDENT# raise RuntimeError ( 'Multiple budgets {!r} found at: {}' . format ( budget , path ) ) #NEWLINE# #UNINDENT# budget_folder = os . path . join ( root , folders . pop ( ) ) #NEWLINE# meta = os . path . join ( budget_folder , 'Budget.ymeta' ) #NEWLINE# with open ( meta , 'r' ) as f : #NEWLINE# #INDENT# data_folder = os . path . join ( budget_folder , json . load ( f ) [ 'relativeDataFolderName' ] ) #NEWLINE# #UNINDENT# devices_folder = os . path . join ( data_folder , 'devices' ) #NEWLINE# device_files = filter ( re . compile ( '^[A-Z]\\.ydevice$' ) . match , os . listdir ( devices_folder ) ) #NEWLINE# devices = [ ] #NEWLINE# for device_file in device_files : #NEWLINE# #INDENT# with open ( os . path . join ( devices_folder , device_file ) , 'r' ) as f : #NEWLINE# #INDENT# device_data = Device ( json . load ( f ) , strict = False ) #NEWLINE# #UNINDENT# guid = device_data . deviceGUID #NEWLINE# budget_file = os . path . join ( data_folder , guid , 'Budget.yfull' ) #NEWLINE# if os . path . isfile ( budget_file ) : #NEWLINE# #INDENT# devices . append ( { #NEWLINE# #INDENT# 'id' : device_data . shortDeviceId , #NEWLINE# 'name' : device_data . friendlyName , #NEWLINE# 'file' : budget_file , #NEWLINE# 'mtime' : os . stat ( budget_file ) . st_mtime , #NEWLINE# 'full' : device_data . hasFullKnowledge , #NEWLINE# #UNINDENT# } ) #NEWLINE# #UNINDENT# #UNINDENT# if ( not devices ) : #NEWLINE# #INDENT# raise RuntimeError ( 'No valid devices found for {!r} at: {}' . format ( budget , path ) ) #NEWLINE# #UNINDENT# if ( device is None ) : #NEWLINE# #INDENT# devices = [ d for d in devices if d [ 'full' ] ] #NEWLINE# if ( not devices ) : #NEWLINE# #INDENT# raise RuntimeError ( 'No devices with full knowledge found' ) #NEWLINE# #UNINDENT# device = max ( devices , key = ( lambda d : d [ 'mtime' ] ) ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# try : #NEWLINE# #INDENT# if ( device in string . ascii_uppercase ) : #NEWLINE# #INDENT# device = [ d for d in devices if ( d [ 'id' ] == device ) ] . pop ( ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# device = [ d for d in devices if ( d [ 'name' ] == device ) ] . pop ( ) #NEWLINE# #UNINDENT# if ( not device [ 'full' ] ) : #NEWLINE# #INDENT# warnings . warn ( 'Device {!r} does not have full knowledge' . format ( d [ 'name' ] ) ) #NEWLINE# #UNINDENT# #UNINDENT# except IndexError : #NEWLINE# #INDENT# raise RuntimeError ( 'No device {!r} for {!r} at: {}' . format ( device , budget , path ) ) #NEWLINE# #UNINDENT# #UNINDENT# self . _path = device [ 'file' ] #NEWLINE# self . _device = device [ 'name' ] #NEWLINE# with open ( self . _path , 'r' ) as f : #NEWLINE# #INDENT# self . _init_data ( json . load ( f ) )