#NEWLINE# def compute_daily_consumption_from_transactions( transactions , window_start , configuration = None ) : #NEWLINE# #INDENT# configuration = ( configuration or ConsumptionConfiguration ( ) ) #NEWLINE# class ConsumptionPeriod ( object ) : #NEWLINE# #INDENT# def __init__( self , tx ) : #NEWLINE# #INDENT# self . start = from_ts ( tx . received_on ) #NEWLINE# self . start_soh = tx . normalized_value #NEWLINE# self . end_soh = None #NEWLINE# self . end = None #NEWLINE# self . consumption = 0 #NEWLINE# self . receipts = 0 #NEWLINE# #UNINDENT# def add( self , tx ) : #NEWLINE# #INDENT# self . consumption += tx . normalized_value #NEWLINE# #UNINDENT# def receipt( self , receipt ) : #NEWLINE# #INDENT# self . receipts += receipt #NEWLINE# #UNINDENT# def close_out( self , tx ) : #NEWLINE# #INDENT# self . end = from_ts ( tx . received_on ) #NEWLINE# self . end_soh = tx . normalized_value #NEWLINE# #UNINDENT# def is_valid( self ) : #NEWLINE# #INDENT# return ( ( self . start_soh + Decimal ( self . receipts ) ) >= self . end_soh ) #NEWLINE# #UNINDENT# @ property #NEWLINE# def length( self ) : #NEWLINE# #INDENT# return span_days ( self . start , self . end ) #NEWLINE# #UNINDENT# @ property #NEWLINE# def normalized_length( self ) : #NEWLINE# #INDENT# return span_days ( max ( self . start , window_start ) , max ( self . end , window_start ) ) #NEWLINE# #UNINDENT# @ property #NEWLINE# def normalized_consumption( self ) : #NEWLINE# #INDENT# return ( ( float ( self . consumption ) * self . normalized_length ) / self . length ) #NEWLINE# #UNINDENT# #UNINDENT# def split_periods( transactions ) : #NEWLINE# #INDENT# period = None #NEWLINE# for tx in transactions : #NEWLINE# #INDENT# if tx . is_checkpoint : #NEWLINE# #INDENT# if period : #NEWLINE# #INDENT# period . close_out ( tx ) #NEWLINE# if ( ( not configuration . exclude_invalid_periods ) or period . is_valid ( ) ) : #NEWLINE# #INDENT# ( yield period ) #NEWLINE# #UNINDENT# #UNINDENT# period = ConsumptionPeriod ( tx ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if tx . is_stockout : #NEWLINE# #INDENT# if period : #NEWLINE# #INDENT# period = None #NEWLINE# #UNINDENT# #UNINDENT# else : #NEWLINE# #INDENT# if ( tx . type == const . TRANSACTION_TYPE_CONSUMPTION ) : #NEWLINE# #INDENT# if period : #NEWLINE# #INDENT# period . add ( tx ) #NEWLINE# #UNINDENT# #UNINDENT# else : #NEWLINE# #INDENT# if ( configuration . exclude_invalid_periods and ( tx . type == const . TRANSACTION_TYPE_RECEIPTS ) ) : #NEWLINE# #INDENT# if ( period and period . start ) : #NEWLINE# #INDENT# period . receipt ( tx . normalized_value ) #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# #UNINDENT# #UNINDENT# #UNINDENT# #UNINDENT# periods = list ( split_periods ( transactions ) ) #NEWLINE# periods = filter ( ( lambda period : period . normalized_length ) , periods ) #NEWLINE# total_consumption = sum ( ( period . normalized_consumption for period in periods ) ) #NEWLINE# total_length = sum ( ( period . normalized_length for period in periods ) ) #NEWLINE# if ( ( len ( periods ) < configuration . min_periods ) or ( total_length < configuration . min_window ) ) : #NEWLINE# #INDENT# return None #NEWLINE# #UNINDENT# return ( ( total_consumption / float ( total_length ) ) if total_length else None )