예제 #1
0
def get_model_key_value_pairs_as_nested_list(mymodel):
    """Returns list of lists containing key-value
    pairs of all of a model's fields."""
    m_list = [['Attribute', 'Value']]
    try:
        if type(mymodel.id) is not int:
            raise AttributeError
    except:
        print 'Function get_model_key_value_pairs_as_nested_list given non-model input, aborting and returning empty list.'
        m_list = []
    else:
        try:
            for z in mymodel._meta.get_all_field_names():
                try:
                    if str(mymodel.__getattribute__(z)) == '':
                        val = '-'
                    else:
                        val = str(mymodel.__getattribute__(z))
                    m_list.append([str(z.replace('_',' ')), val])
                except AttributeError:
                    pass
        except:
            m = Message(when=timezone.now(),
                        message_type='Code Error',
                        subject='Retrieve data failed.',
                        comment='Object %s of type %s unable to retrieve list of lists of its key-value pairs, aborting and returning empty list.' % (mymodel.id, mymodel.__class__()))
            m.save()
            mymodel.messages.add(m)
            print m
            m_list = []
    return m_list
 def get_savings_df(self,df):
     """function(df)
     
     Given dataframe with
     monthly index, returns
     dataframe with new
     columns for consumption
     savings, peak demand
     savings, and cost
     savings."""
     try:
         d = self.get_savings_dictionary()
         df['Consumption Savings'] = 0.0
         df['Peak Demand Savings'] = 0.0
         df['Cost Savings'] = 0.0
         for i in range(0,len(df)):
             if self.when < df['End Date'][i]:
                 df['Consumption Savings'][i:i+1] = d[str(df.index[i].month)]['Consumption Savings']
                 df['Peak Demand Savings'][i:i+1] = d[str(df.index[i].month)]['Peak Demand Savings']
                 df['Cost Savings'][i:i+1] = d[str(df.index[i].month)]['Cost Savings']
     except:
         m = Message(when=timezone.now(),
                     message_type='Code Error',
                     subject='Calculation failed.',
                     comment='EfficiencyMeasure %s get_savings_df failed, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     return df
 def get_cost_df(self, df):
     try:
         if self.apply_to_base_revenue:
             df['Rider Cost'] = df['Base Revenue Cost'] * float(self.percent_of_base_revenue)
         elif self.apply_to_total_revenue:
             df['Rider Cost'] = df['Total Revenue Cost'] * float(self.percent_of_total_revenue)
         elif self.apply_to_consumption:
             for i in range(0,len(df)):
                 if df['End Date'][i].month in [6,7,8,9]:
                     df['Rider Cost'][i:i+1] = df['Consumption'][i:i+1] * float(self.summer_cost_per_kWh)
                 elif df['End Date'][i].month in [1,2,3,4,5,10,11,12]:
                     df['Rider Cost'][i:i+1] = df['Consumption'][i:i+1] * float(self.winter_cost_per_kWh)
                 else:
                     raise TypeError
         else:
             raise TypeError
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Error',
                 subject='Calculation failed.',
                 comment='GAPowerRider %s get_cost_df failed, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     return df
 def get_billing_demand_df(self, df, billx):
     """function(df, billx)
     
     Given dataframe with
     monthly index, Peak
     Demand, and Consumption,
     returns dataframe with new
     column of Calculated
     Billing Demand."""
     try:
         summer_months = self.get_summer_months()
         winter_months = self.get_winter_months()
         df['Calculated Billing Demand'] = float(0.0)
         df = df.sort_index()
         billxdf = billx.get_monther_period_dataframe()
         if billxdf is not None: billxdf = billxdf.sort_index()
         combined_df = pd.concat([df, billxdf])
         combined_df = combined_df[[not(i) for i in combined_df['Peak Demand'].apply(math.isnan)]]
         combined_df = combined_df.sort_index()
         
         df_window = combined_df[max(0,len(combined_df)-int(self.billing_demand_sliding_month_window)):]
         
         for i in range(0,len(df)):
             summer_peaks = df_window['Peak Demand'][[x.month in summer_months for x in df_window['End Date']]]
             if len(summer_peaks)==0:
                 summer_max = 0
             else:
                 summer_max = summer_peaks.max()
             winter_peaks = df_window['Peak Demand'][[x.month in winter_months for x in df_window['End Date']]]
             if len(winter_peaks)==0:
                 winter_max = 0
             else:
                 winter_max = winter_peaks.max()
             
             if df['End Date'][i].month in summer_months: #might normally use df.index[i].month but GAPower uses meter read date
                 df['Calculated Billing Demand'][i:i+1] = max_with_NaNs([
                         df['Peak Demand'][i],
                         float(self.summer_summer_threshold) * summer_max,
                         float(self.summer_winter_threshold) * winter_max,
                         float(self.contract_minimum_demand),
                         float(self.minimum_fraction_contract_capacity) * float(self.excess_kW_threshold),
                         float(self.absolute_minimum_demand)  ])
             if df['End Date'][i].month in winter_months:
                 df['Calculated Billing Demand'][i:i+1] = max_with_NaNs([
                         float(self.winter_summer_threshold) * summer_max,
                         float(self.winter_winter_threshold) * winter_max,
                         float(self.contract_minimum_demand),
                         float(self.minimum_fraction_contract_capacity) * float(self.excess_kW_threshold),
                         float(self.absolute_minimum_demand)  ])
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Error',
                 subject='Calculation failed.',
                 comment='GAPowerPandL %s get_billing_demand_df unable to calculate billing demand, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     return df
예제 #5
0
 def save(self, *args, **kwargs):
     if self.id is None:
         super(UserProfile, self).save(*args, **kwargs)
         tnow = timezone.now()
         m = Message(when = tnow,
                     message_type = 'Model Info',
                     subject = 'Model created.',
                     comment = 'This UserProfile was created on %s.' % tnow)
         m.save()
         self.messages.add(m)
     super(UserProfile, self).save(*args, **kwargs)
예제 #6
0
 def get_cost_df(self, df):
     try:
         
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Error',
                 subject='Calculation failed.',
                 comment='GAPowerRider %s get_cost_df failed, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     return df
예제 #7
0
 def get_billing_demand_df(self, df, billx):
     """function(df, billx)
     
     Given dataframe with
     monthly index, Peak
     Demand, and Consumption,
     returns dataframe with new
     column of Calculated
     Billing Demand."""
     try:
         
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Error',
                 subject='Calculation failed.',
                 comment='GAPowerPandL %s get_billing_demand_df unable to calculate billing demand, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     return df
 def get_summer_months(self):
     """No inputs.  Returns
     list of summer month
     indices 1-12."""
     try:
         if self.summer_end_month == Decimal(0.0) or self.summer_start_month == Decimal(0.0):
             raise TypeError
         if self.summer_end_month < self.summer_start_month:
             summer_months = range(1,self.summer_end_month+1)
             b = range(self.summer_start_month,13)
             summer_months.extend(b)
         else:
             summer_months = range(self.summer_start_month,self.summer_end_month+1)
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Error',
                 subject='Calculation failed.',
                 comment='GAPowerPandL %s get_summer_months failed, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
         summer_months = None
     return summer_months
예제 #9
0
 def get_cost_df(self, df, billx=None):
     """function(df, billx=None)
     
     Given dataframe with monthly
     index and Peak Demand and
     Consumption, along with a
     BILLx Monther whose data
     will be retrieved for use
     in calculating billing
     demand from historical data,
     returns dataframe with
     Calculated Cost and Billing
     Demand columns."""
     try:
         
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Error',
                 subject='Calculation failed.',
                 comment='XXXXX %s get_cost_df unable to calculate billing demand, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     return df
 def pass_limitation_of_service(self, df):
     """function(df)
     
     Given dataframe of Peak
     Demands, returns boolean
     indicating whether monthly
     demands satisfy rate
     schedule's limitation of
     service. Returns None if
     too few months given.
     """
     
     try:
         if ( df['Peak Demand'].count() < self.lim_service_sliding_month_window or
              float('NaN') in df['Peak Demand'].sort_index()[-1:-13:-1] ):
             m = Message(when=timezone.now(),
                     message_type='Code Warning',
                     subject='Missing data.',
                     comment='GAPowerPandL %s pass_limitation_of_service given too few peak demands, function aborted.' % self.id)
             m.save()
             self.messages.add(m)
             print m
             answer = None
         else:
             summer_months = self.get_summer_months()
             limit_1 = float(self.limitation_of_service_winter_percent) * df['Peak Demand'][[x.month not in summer_months for x in df['End Date']]].max()
             limit_2 = float(self.limitation_of_service_summer_percent) * df['Peak Demand'][[x.month in summer_months for x in df['End Date']]].max()
             answer = ( (max_with_NaNs([limit_1, limit_2]) < float(self.limitation_of_service_max_kW)) and
                        (max_with_NaNs([limit_1, limit_2]) > float(self.limitation_of_service_min_kW)) )
     except:
             m = Message(when=timezone.now(),
                     message_type='Code Error',
                     subject='Calculation failed.',
                     comment='GAPowerPandL %s pass_limitation_of_service unable to calculate, function aborted.' % self.id)
             m.save()
             self.messages.add(m)
             print m
             answer = None
     return answer
    def get_cost_df(self, df, billx=None):
        """function(df)
        
        Given dataframe with monthly
        index and Peak Demand and
        Consumption, returns dataframe
        with Calculated Cost column."""
        try:
            df['Billing Demand'] = df['Billing Demand'].apply(Decimal)
            df['Peak Demand'] = df['Peak Demand'].apply(Decimal)
            df['Consumption'] = df['Consumption'].apply(Decimal)

            df['Consumption Cost'] = df['Consumption']*self.therm_rate
        except:
            m = Message(when=timezone.now(),
                    message_type='Code Warning',
                    subject='Calculation failed.',
                    comment='InfiniteEnergyGAGas %s get_cost_df unable to calculate consumption charges, function aborted.' % self.id)
            m.save()
            self.messages.add(m)
            print m
        else:
            try:
                df['Base Cost'] = self.basic_service_charge
            except:
                m = Message(when=timezone.now(),
                        message_type='Code Error',
                        subject='Calculation failed.',
                        comment='InfiniteEnergyGAGas %s get_cost_df unable to calculate base charges, function aborted.' % self.id)
                m.save()
                self.messages.add(m)
                print m
            else:
                df['Calculated Cost'] = (  df['Base Cost'] + df['Consumption Cost']
                                               ) * (Decimal(1.0) + self.tax_percentage)
        return df
 def get_cost_df(self, df, billx=None):
     """function(df)
     
     Given dataframe with monthly
     index and Peak Demand and
     Consumption, returns dataframe
     with Calculated Cost column."""
     try:
         df['Billing Demand'] = df['Billing Demand']
         df['Peak Demand'] = df['Peak Demand']
         df['Consumption'] = df['Consumption']
         
         df['k1'] = [min(float(self.tier1),df['Consumption'][i]) for i in range(0,len(df))]
         df['k2'] = [min(float(self.tier2)-float(self.tier1),df['Consumption'][i]-df['k1'][i]) for i in range(0,len(df))]
         df['k3'] = [min(float(self.tier3)-float(self.tier2),df['Consumption'][i]-df['k1'][i]-df['k2'][i]) for i in range(0,len(df))]
         
         df['checksum'] = df['k1'] + df['k2'] + df['k3']
         sum1 = df['checksum'].sum()
         sum2 = df['Consumption'].sum()
         if min(sum1,sum2)/max(sum1,sum2) < float(0.999):
             m = Message(when=timezone.now(),
                     message_type='Code Error',
                     subject='Calculation failed.',
                     comment='CityOfATLWWW %s get_cost_df computed tier consumptions that do not sum to total consumption, function aborted.' % self.id)
             m.save()
             self.messages.add(m)
             print m
             raise TypeError
         
         df['Consumption Cost'] = df['k1']*float(self.rate1)/748.0 + df['k2']*float(self.rate2)/748.0 + df['k3']*float(self.rate3)/748.0
     except:
         m = Message(when=timezone.now(),
                 message_type='Code Warning',
                 subject='Calculation failed.',
                 comment='CityOfATLWWW %s get_cost_df unable to calculate consumption charges, function aborted.' % self.id)
         m.save()
         self.messages.add(m)
         print m
     else:
         try:
             df['Base Cost'] = float(self.base_charge)
         except:
             m = Message(when=timezone.now(),
                     message_type='Code Error',
                     subject='Calculation failed.',
                     comment='CityOfATLWWW %s get_cost_df unable to calculate base charges, function aborted.' % self.id)
             m.save()
             self.messages.add(m)
             print m
         else:
             try:
                 df['Security Surcharge Cost'] = df['Consumption'] * float(self.security_surcharge) / 748.0
             except:
                 m = Message(when=timezone.now(),
                         message_type='Code Error',
                         subject='Calculation failed.',
                         comment='CityOfATLWWW %s get_cost_df unable to calculate rider charges, function aborted.' % self.id)
                 m.save()
                 self.messages.add(m)
                 print m
             else:
                 df['Calculated Cost'] = (  df['Base Cost'] + df['Consumption Cost'] + 
                                            df['Security Surcharge Cost']
                                            ) * (float(1.0) + float(self.tax_percentage))
     return df
 def load_model_data_file(self):
     #this block is for new models; does not yet check like it needs to
     #need to identify difference between existing and new
     modeltypes = ['Account','WeatherStation','OperatingSchedule','Utility',
                   'Building','RateSchedule','Meter','Equipment','Space',
                   'Event','Reader','RooftopUnit','UnitSchedule']
     try:
         self.model_data_file_for_upload.open()
         self.model_data_file_for_upload.close()
     except:
         m = Message(when=timezone.now(),
                     message_type='Code Error',
                     subject='Open model data file failed.',
                     comment='ManagementAction unable to load new model data, function aborted.')
         m.save()
         self.messages.add(m)
         print m
     else:
         try:    #---this block creates a dataframe for each sheet and dumps into dict
             self.model_data_file_for_upload.open()
             uploaded_file = pd.ExcelFile(self.model_data_file_for_upload)
             data = {}
             for modeltype in modeltypes:
                 data[modeltype] = uploaded_file.parse(modeltype, index_col = None)
             self.model_data_file_for_upload.close()
         except:
             m = Message(when=timezone.now(),
                         message_type='Code Error',
                         subject='Parse uploaded file to dataframe failed.',
                         comment='ManagementAction unable to parse uploaded file, aborting.')
             m.save()
             self.messages.add(m)
             print m
         else:   #---identifies field names of FKs on other models for a given model type
                 #---e.g. Buildings and Meters have FK to Account through the attribute "account"
             relationdict = {'Account':{'Building':'account',
                                        'Meter':'account'},
                             'WeatherStation':{'Building':'weather_station',
                                               'Meter':'weather_station'},
                             'OperatingSchedule':{'Equipment':'schedule',
                                                  'RooftopUnit':'schedule'},
                             'Utility':{'RateSchedule':'utility',
                                        'Meter':'utility'},
                             'RateSchedule':{'Meter':'rate_schedule'},
                             'Building':{'Space':'building'}}
             #---first load all models that already exist and are used as FKs for other models
             for modeltype in relationdict:
                 try:
                     modelclass = get_model('BuildingSpeakApp',modeltype)
                 except:
                     m = Message(when=timezone.now(),
                                 message_type='Code Error',
                                 subject='Retrieve class failed.',
                                 comment='ManagementAction unable to retrieve class type %s, aborting and moving to next class type.' % modeltype)
                     m.save()
                     self.messages.add(m)
                     print m
                 else:
                     try:
                         for df in relationdict[modeltype]:  #---relationdict tells us where FK relations are that we need to visit
                             if ( (df in data) and
                                  (len(data[df][relationdict[modeltype][df] + '_value']) > 0)  ):
                                 #---forcing dtype to not be float (if all floats or ints, pandas will create a float dtype and not allow dumping a model instance)
                                 data[df][relationdict[modeltype][df] + '_value'] = data[df][relationdict[modeltype][df] + '_value'].astype('object')
                                 for i in range(0,len(data[df][relationdict[modeltype][df] + '_value'])):
                                     id_value = data[df][relationdict[modeltype][df] + '_value'][i:i+1][0]
                                     if type(id_value) is float:     #---if id_value is integer (pandas makes them floats), it already exists, so load it
                                         try:
                                             model_i = modelclass.objects.get(pk = id_value)
                                         except:
                                             m = Message(when=timezone.now(),
                                                         message_type='Code Error',
                                                         subject='Retrieve model failed.',
                                                         comment='ManagementAction unable to retrieve %s %s on row %d, aborting and moving to next row.' % (modeltype, str(id_value), i))
                                             m.save()
                                             self.messages.add(m)
                                             print m
                                         else:
                                             try:
                                                 data[df][relationdict[modeltype][df] + '_value'][i:i+1][0] = model_i
                                             except:
                                                 m = Message(when=timezone.now(),
                                                             message_type='Code Error',
                                                             subject='Swap model for string PK failed.',
                                                             comment='ManagementAction unable to replace PK with %s %d on row %d, aborting and moving to next row.' % (modeltype, id_value, i))
                                                 m.save()
                                                 self.messages.add(m)
                                                 print m
                     except:
                         m = Message(when=timezone.now(),
                                     message_type='Code Error',
                                     subject='Swap models for string PKs failed.',
                                     comment='ManagementAction unable to replace PKs with model instances, aborting and moving on; note that some models in uploaded sheet requiring these models to be loaded will fail.')
                         m.save()
                         self.messages.add(m)
                         print m
             #---step through loading all new models for modeltype, then move to next type
             for modeltype in modeltypes:
                 try:
                     modelclass = get_model('BuildingSpeakApp',modeltype)
                 except:
                     m = Message(when=timezone.now(),
                                 message_type='Code Error',
                                 subject='Retrieve class failed.',
                                 comment='ManagementAction unable to retrieve class type %s, aborting and moving to next class type.' % modeltype)
                     m.save()
                     self.messages.add(m)
                     print m
                 else:
                     data[modeltype]['id_value'] = data[modeltype]['id_value'].astype('object') #---forcing dtype to not be float (if all floats or ints, pandas will create a float dtype and not allow dumping a model instance)
                     modeldf = data[modeltype]        #---extract the modeltype's dataframe
                     for i in range(0,len(modeldf)):  #---loop through all rows; each row is a model
                         id_value = data[modeltype]['id_value'][i:i+1][0]    #---get integer model ID
                         if type(id_value) is float:     #---if id_value is integer (pandas makes them floats), it already exists, so load it
                             try:
                                 existingmodel = modelclass.objects.get(pk = id_value)
                             except:
                                 m = Message(when=timezone.now(),
                                             message_type='Code Error',
                                             subject='Retrieve model failed.',
                                             comment='ManagementAction unable to retrieve %s %d on row %d that should exist according to uploaded file, aborting and moving to next row.' % (modeltype, id_value, i))
                                 m.save()
                                 self.messages.add(m)
                                 print m
                             else:
                                 try:   #---extract field names and values, then morph to dict for loading
                                     fields = modeldf[modeldf.columns[0::2]][i:i+1]
                                     values = modeldf[modeldf.columns[1::2]][i:i+1]
                                     m = pd.Series(values.values[0], index = fields)
                                     md = m[(m.notnull()) & (m.index<>'id')].to_dict()
                                     for k, v in md.items():
                                         existingmodel.__setattr__(k, v)  #---update existing model
                                     existingmodel.save()
                                 except:
                                     m = Message(when=timezone.now(),
                                                 message_type='Code Error',
                                                 subject='Model update failed.',
                                                 comment='ManagementAction unable to update %s %d on row %d, aborting and moving to next row.' % (modeltype, id_value, i))
                                     m.save()
                                     self.messages.add(m)
                                     print m
                                 else:
                                     try:    #---then overwrite integer model ID (id_value) wherever it appears
                                         data[modeltype]['id_value'][i:i+1][0] = existingmodel #---overwrite integer model ID with model instance
                                         if modeltype in relationdict:
                                             for df in relationdict[modeltype]:  #---relationdict tells us where FK relations are that we need to visit
                                                 if len(data[df][relationdict[modeltype][df] + '_value']) > 0:
                                                     data[df][relationdict[modeltype][df] + '_value'] = data[df][relationdict[modeltype][df] + '_value'].apply(self.swap,**{'c':id_value,'y':existingmodel})
                                     except:
                                         m = Message(when=timezone.now(),
                                                     message_type='Code Error',
                                                     subject='Swap model for string PK failed.',
                                                     comment='ManagementAction unable to swap models for integer PKs after loading %s %d on row %d of uploaded sheet, aborting and moving to next row.' % (modeltype, id_value, i))
                                         m.save()
                                         self.messages.add(m)
                                         print m
                                     else:
                                         m = Message(when=timezone.now(),
                                                     message_type='Code Success',
                                                     subject='Loaded and/or updated model.',
                                                     comment='ManagementAction loaded and, if necessary, updated %s %d on row %d of uploaded sheet, moving to next row.' % (modeltype, id_value, i))
                                         m.save()
                                         self.messages.add(m)
                                         print m
                         elif type(id_value) is unicode:#---if id_value is unicode, it's new, so create it
                             try:    #---extract field names and values, then morph to dict for loading
                                 fields = modeldf[modeldf.columns[0::2]][i:i+1]
                                 values = modeldf[modeldf.columns[1::2]][i:i+1]
                                 m = pd.Series(values.values[0], index = fields)
                                 md = m[(m.notnull()) & (m.index<>'id')].to_dict()
                                 newmodel = modelclass(**md)
                                 newmodel.save()
                             except:
                                 m = Message(when=timezone.now(),
                                             message_type='Code Error',
                                             subject='Model load failed.',
                                             comment='ManagementAction unable to load %s on row %d of uploaded sheet, aborting and moving to next row.' % (modeltype, i))
                                 m.save()
                                 self.messages.add(m)
                                 print m
                             else:
                                 if newmodel.id is not None:
                                     m = Message(when=timezone.now(),
                                                 message_type='Code Success',
                                                 subject='Model created.',
                                                 comment='ManagementAction successfully created %s %d from row %d of uploaded sheet, moving on.' % (modeltype, newmodel.id, i))
                                     m.save()
                                     self.messages.add(m)
                                     print m
                                     c = data[modeltype]['id_value'][i:i+1][0]   #---capture string name
                                     data[modeltype]['id_value'][i:i+1][0] = newmodel #---overwrite string name with model instance
                                     try:    #---then overwrite string name (c) wherever else it appears
                                         if modeltype in relationdict:
                                             for df in relationdict[modeltype]:  #---relationdict tells us where FK relations are that we need to visit
                                                 if len(data[df][relationdict[modeltype][df] + '_value']) > 0:
                                                     data[df][relationdict[modeltype][df] + '_value'] = data[df][relationdict[modeltype][df] + '_value'].apply(self.swap,**{'c':c,'y':newmodel})
                                     except:
                                         m = Message(when=timezone.now(),
                                                     message_type='Code Error',
                                                     subject='Swap model for string name failed.',
                                                     comment='ManagementAction unable to swap models for string names after creating %s on row %d of uploaded sheet, aborting and moving to next row.' % (modeltype, i))
                                         m.save()
                                         self.messages.add(m)
                                         print m
                                 else:
                                     m = Message(when=timezone.now(),
                                                 message_type='Code Error',
                                                 subject='Model save failed.',
                                                 comment='ManagementAction attempt to save %s from row %d of uploaded sheet did not return an ID, moving to next row.' % (modeltype, i))
                                     m.save()
                                     self.messages.add(m)
                                     print m
                         else:
                             m = Message(when=timezone.now(),
                                         message_type='Code Error',
                                         subject='Function received bad arguments.',
                                         comment='ManagementAction received a PK from %s row %d of uploaded sheet that was neither float (int) nor unicode, aborting and moving to next row.' % (modeltype, i))
                             m.save()
                             self.messages.add(m)
                             print m
                                 
             #---now all models have been loaded
             #---now replace models with their IDs for storing in "_processed" Excel file
             try:
                 for modeltype in modeltypes:
                     if modeltype in relationdict:
                         for df in relationdict[modeltype]:  #now replace models with id's
                             if len(data[df][relationdict[modeltype][df] + '_value']) > 0:
                                 data[df][relationdict[modeltype][df] + '_value'] = data[df][relationdict[modeltype][df] + '_value'].apply(self.swapid)
                     if len(data[modeltype]['id_value']) > 0:
                         data[modeltype]['id_value'] = data[modeltype]['id_value'].apply(self.swapid)
             except:
                 m = Message(when=timezone.now(),
                             message_type='Code Error',
                             subject='Swap string PKs for models failed.',
                             comment='ManagementAction unable to replace models with model IDs for exporting revised load file, aborting, though models should have still been created.')
                 m.save()
                 self.messages.add(m)
                 print m
             else:
                 try:
                     filename = 'temp_processed.xlsx'
                     writer = pd.ExcelWriter(filename)
                     for modeltype in modeltypes:
                         data[modeltype].to_excel(writer, sheet_name = modeltype)
                     writer.save()
                     astring = open(filename,'rb').read()
                     mycontent = ContentFile(astring)
                     self.load_model_data = False
                     self.processed_file.save(name = filename, content = mycontent, save = False)
                 except:
                     m = Message(when=timezone.now(),
                                 message_type='Code Error',
                                 subject='Write processed file failed.',
                                 comment='ManagementAction unable to write revised load file, aborting, though models should have still been created.')
                     m.save()
                     self.messages.add(m)
                     print m
                 else:
                     m = Message(when=timezone.now(),
                                 message_type='Code Success',
                                 subject='Created processed file.',
                                 comment='ManagementAction successfully created and stored a processed file containing original model data plus new model IDs.')
                     m.save()
                     self.messages.add(m)
                     print m
                     
     self.load_model_data = False
 def create_import_template_file(self):
     try:
         filename = 'BuildingSpeak Import File.xlsx'
         writer = pd.ExcelWriter(filename)
     
 #        m2ms_Account = [i.name for i in Account._meta.many_to_many]
         fields_Account = [i.name for i in Account._meta.fields]
 #        m2ms_WeatherStation = [i.name for i in WeatherStation._meta.many_to_many]
         fields_WeatherStation = [i.name for i in WeatherStation._meta.fields]
 #        m2ms_OperatingSchedule = [i.name for i in OperatingSchedule._meta.many_to_many]
         fields_OperatingSchedule = [i.name for i in OperatingSchedule._meta.fields]
 #        m2ms_Utility = [i.name for i in Utility._meta.many_to_many]
         fields_Utility = [i.name for i in Utility._meta.fields]
 #        m2ms_Building = [i.name for i in Building._meta.many_to_many]
         fields_Building = [i.name for i in Building._meta.fields]
 #        m2ms_RateSchedule = [i.name for i in RateSchedule._meta.many_to_many]
         fields_RateSchedule = [i.name for i in RateSchedule._meta.fields]
 #        m2ms_Meter = [i.name for i in Meter._meta.many_to_many]
         fields_Meter = [i.name for i in Meter._meta.fields]
 #        m2ms_Equipment = [i.name for i in Equipment._meta.many_to_many]
         fields_Equipment = [i.name for i in Equipment._meta.fields]
 #        m2ms_Space = [i.name for i in Space._meta.many_to_many]
         fields_Space = [i.name for i in Space._meta.fields]
 #        m2ms_Reader = [i.name for i in Reader._meta.many_to_many]
         fields_Reader = [i.name for i in Reader._meta.fields]
 #        m2ms_RooftopUnit = [i.name for i in RooftopUnit._meta.many_to_many]
         fields_RooftopUnit = [i.name for i in RooftopUnit._meta.fields]
 #        m2ms_UnitSchedule = [i.name for i in UnitSchedule._meta.many_to_many]
         fields_UnitSchedule = [i.name for i in UnitSchedule._meta.fields]
     
         df_Account = pd.DataFrame()
         for i in fields_Account:
             df_Account[i] = None
             df_Account[i + '_value'] = None
 #        for i in m2ms_Account:
 #            df_Account[i] = None
 #            df_Account[i + '_value'] = None
         df_Account.to_excel(writer, sheet_name='Account')
         
         df_WeatherStation = pd.DataFrame()
         for i in fields_WeatherStation:
             df_WeatherStation[i] = None
             df_WeatherStation[i + '_value'] = None
 #        for i in m2ms_WeatherStation:
 #            df_WeatherStation[i] = None
 #            df_WeatherStation[i + '_value'] = None
         df_WeatherStation.to_excel(writer, sheet_name='WeatherStation')
         
         df_OperatingSchedule = pd.DataFrame()
         for i in fields_OperatingSchedule:
             df_OperatingSchedule[i] = None
             df_OperatingSchedule[i + '_value'] = None
 #        for i in m2ms_OperatingSchedule:
 #            df_OperatingSchedule[i] = None
 #            df_OperatingSchedule[i + '_value'] = None
         df_OperatingSchedule.to_excel(writer, sheet_name='OperatingSchedule')
         
         df_Utility = pd.DataFrame()
         for i in fields_Utility:
             df_Utility[i] = None
             df_Utility[i + '_value'] = None
 #        for i in m2ms_Utility:
 #            df_Utility[i] = None
 #            df_Utility[i + '_value'] = None
         df_Utility.to_excel(writer, sheet_name='Utility')
         
         df_Building = pd.DataFrame()
         for i in fields_Building:
             df_Building[i] = None
             df_Building[i + '_value'] = None
 #        for i in m2ms_Building:
 #            df_Building[i] = None
 #            df_Building[i + '_value'] = None
         df_Building.to_excel(writer, sheet_name='Building')
         
         df_RateSchedule = pd.DataFrame()
         for i in fields_RateSchedule:
             df_RateSchedule[i] = None
             df_RateSchedule[i + '_value'] = None
 #        for i in m2ms_RateSchedule:
 #            df_RateSchedule[i] = None
 #            df_RateSchedule[i + '_value'] = None
         df_RateSchedule.to_excel(writer, sheet_name='RateSchedule')
         
         df_Meter = pd.DataFrame()
         for i in fields_Meter:
             df_Meter[i] = None
             df_Meter[i + '_value'] = None
 #        for i in m2ms_Meter:
 #            df_Meter[i] = None
 #            df_Meter[i + '_value'] = None
         df_Meter.to_excel(writer, sheet_name='Meter')
         
         df_Equipment = pd.DataFrame()
         for i in fields_Equipment:
             df_Equipment[i] = None
             df_Equipment[i + '_value'] = None
 #        for i in m2ms_Equipment:
 #            df_Equipment[i] = None
 #            df_Equipment[i + '_value'] = None
         df_Equipment.to_excel(writer, sheet_name='Equipment')
         
         df_Space = pd.DataFrame()
         for i in fields_Space:
             df_Space[i] = None
             df_Space[i + '_value'] = None
 #        for i in m2ms_Space:
 #            df_Space[i] = None
 #            df_Space[i + '_value'] = None
         df_Space.to_excel(writer, sheet_name='Space')
         
         df_Reader = pd.DataFrame()
         for i in fields_Reader:
             df_Reader[i] = None
             df_Reader[i + '_value'] = None
 #        for i in m2ms_Reader:
 #            df_Reader[i] = None
 #            df_Reader[i + '_value'] = None
         df_Reader.to_excel(writer, sheet_name='Reader')
         
         df_RooftopUnit = pd.DataFrame()
         for i in fields_RooftopUnit:
             df_RooftopUnit[i] = None
             df_RooftopUnit[i + '_value'] = None
 #        for i in m2ms_RooftopUnit:
 #            df_RooftopUnit[i] = None
 #            df_RooftopUnit[i + '_value'] = None
         df_RooftopUnit.to_excel(writer, sheet_name='RooftopUnit')
         
         df_UnitSchedule = pd.DataFrame()
         for i in fields_UnitSchedule:
             df_UnitSchedule[i] = None
             df_UnitSchedule[i + '_value'] = None
 #        for i in m2ms_UnitSchedule:
 #            df_UnitSchedule[i] = None
 #            df_UnitSchedule[i + '_value'] = None
         df_UnitSchedule.to_excel(writer, sheet_name='UnitSchedule')
         
         writer.save()
 
         astring=open(filename,'rb').read()
         mycontent = ContentFile(astring)
         self.create_a_new_import_file = False
         self.import_file.save(name=filename, content=mycontent, save=False)
         m = Message(when=timezone.now(),
                     message_type='Code Success',
                     subject='Created upload template.',
                     comment='ManagementAction successfully created new upload template file.')
         m.save()
         self.messages.add(m)
         print m
         self.create_a_new_import_file = False
     except:
         m = Message(when=timezone.now(),
                     message_type='Code Error',
                     subject='Create upload template failed.',
                     comment='ManagementAction unable to create new upload template file, function aborted.')
         m.save()
         self.messages.add(m)
         print m
         self.create_a_new_import_file = False
         self.save()
    def get_cost_df(self, df, billx=None):
        """function(df, billx=None)
        
        Given dataframe with monthly
        index and Peak Demand and
        Consumption, along with a
        BILLx Monther whose data
        will be retrieved for use
        in calculating billing
        demand from historical data,
        returns dataframe with
        Calculated Cost and Billing
        Demand columns."""
        try:
            if self.rate_type == 'fixed $/unit':
                df['Calculated Cost'] = df['Consumption'] * float(self.rate_per_unit)
            elif self.rate_type == 'same-month average':
                if billx is not None:
                    billxdf = billx.get_monther_period_dataframe()
                else:
                    billxdf = None
                if billxdf is not None: billxdf = billxdf.sort_index()
                combined_df = pd.concat([df, billxdf])
                combined_df = combined_df.sort_index()
                combined_df = combined_df[max(0,len(combined_df)-float(self.max_number_same_months)*12):]
                if len(combined_df) < 12: raise ValueError
                
                avg_rates = {}
                for i in range(1,13):
                    avg_rates[i] = (combined_df['Cost (act)'][[m.month==i for m in combined_df.index]].sum() /
                                    combined_df['Consumption (act)'][[m.month==i for m in combined_df.index]].sum() )
                    #actuals used to calculate rates, but generic Consumption used elsewhere as it will
                    #be various versions (base, exp, etc.) as Meter cycles through them to calculate
                    #costs on all of them
                
                df['month number'] = df.index.map(lambda i: i.month)
                df['avg rate'] = df['month number'].apply(lambda i: avg_rates[i])
                df['Calculated Cost'] = df['Consumption'] * df['avg rate']
            elif self.rate_type == 'moving average':
                if billx is not None:
                    billxdf = billx.get_monther_period_dataframe()
                else:
                    billxdf = None
                if billxdf is not None: billxdf = billxdf.sort_index()
                combined_df = pd.concat([df, billxdf])
                combined_df = combined_df.sort_index()
                
                #need to remove forecasted months first in order to use Consumption(act) and Cost(act)
                combined_df['Cost (act) is NaN'] = combined_df['Cost (act)'].apply(math.isnan)
                combined_df['Consumption (act) is NaN'] = combined_df['Consumption (act)'].apply(math.isnan)
                temp = [[combined_df['Cost (act) is NaN'][i],
                         combined_df['Consumption (act) is NaN'][i]] for i in range(0,len(combined_df))]
                combined_df['IsNotForecasted'] = [not(i[0] and i[1]) for i in temp]
                combined_df = combined_df[combined_df['IsNotForecasted']]

                combined_df = combined_df[max(0,len(combined_df)-float(self.moving_average_window_length)):]
                if len(combined_df) < 1:
                    m = Message(when=timezone.now(),
                            message_type='Code Warning',
                            subject='Calculation failed.',
                            comment='GeneralConsumption %s get_cost_df found no data in moving average window, costs set to NaNs.' % self.id)
                    m.save()
                    self.messages.add(m)
                    print m
                    df['Calculated Cost'] = float('NaN')
                else:
                    df['Calculated Cost'] = df['Consumption'] * combined_df['Cost (act)'].sum() / combined_df['Consumption (act)'].sum()
                    #actuals used to calculate rates, but generic Consumption used elsewhere as it will
                    #be various versions (base, exp, etc.) as Meter cycles through them to calculate
                    #costs on all of them
                
        except:
            m = Message(when=timezone.now(),
                    message_type='Code Error',
                    subject='Calculation failed.',
                    comment='GeneralConsumption %s get_cost_df unable to calculate costs, function aborted.' % self.id)
            m.save()
            self.messages.add(m)
            print m
        return df
def update_readers(modelinstance):
    """Function to update all Readers
    on given Account, Building,
    Space, Meter, or Equipment model."""
    try:
        modeltype = modelinstance.__repr__()[1:].partition(':')[0]
    except:
        m = Message(when=timezone.now(),
                    message_type='Code Error',
                    subject='Function received bad arguments.',
                    comment='Model %s of type %s given to update_readers function, expected Account, Building, Space, Meter, or Equipment, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
        m.save()
        modelinstance.messages.add(m)
        print m
    else:
        if modeltype not in ['Account', 'Building', 'Space', 'Meter', 'Equipment']:
            m = Message(when=timezone.now(),
                        message_type='Code Error',
                        subject='Function received bad arguments.',
                        comment='Model %s of type %s given to update_readers function, expected Account, Building, Space, Meter, or Equipment, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
            m.save()
            modelinstance.messages.add(m)
            print m
        else:
            if modelinstance.observed_file_track:
                try:
                    df = pd.read_csv(modelinstance.observed_file.url,
                                     skiprows=modelinstance.observed_file_skiprows,
                                     parse_dates=True,
                                     index_col=modelinstance.observed_file_column_for_indexing)
                except:
                    m = Message(when=timezone.now(),
                               message_type='Code Error',
                               subject='File not found.',
                               comment='observed_file of model %s of type %s not found by update_readers function, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                    m.save()
                    modelinstance.messages.add(m)
                    print m
                else:
                    try:
                        df = df.sort_index()
                        df.index = df.index - timedelta(hours=float(modelinstance.observed_file_GMT_offset))
                        df.index = df.index.tz_localize('UTC')
                    except:
                        m = Message(when=timezone.now(),
                                   message_type='Code Error',
                                   subject='Adjust dataframe index failed.',
                                   comment='Failed to adjust index of observed_file dataframe of model %s of type %s in update_readers function, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                        m.save()
                        modelinstance.messages.add(m)
                        print m
                    else:
                        column_headers = df.columns
                        df.columns = range(0,len(df.columns))
                        try:
                            for r in modelinstance.readers.filter(source=1).filter(active=True):
                                try:
                                    rts = r.get_reader_time_series()
                                    rts = rts.sort_index()
                                    if len(df[r.column_index]) < 1:  #nothing to add, so abort
                                        m = Message(when=timezone.now(),
                                                   message_type='Code Warning',
                                                   subject='No data.',
                                                   comment='No new data found for Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                        m.save()
                                        modelinstance.messages.add(m)
                                        r.messages.add(m)
                                        print m                                    
                                    elif len(rts) < 1: #nothing existing, so add all new stuff
                                        r.load_reader_time_series(df[r.column_index])
                                        r.column_header = column_headers[r.column_index]
                                        r.save()
                                        m = Message(when=timezone.now(),
                                                   message_type='Code Success',
                                                   subject='Updated model.',
                                                   comment='Successfully updated Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                        m.save()
                                        modelinstance.messages.add(m)
                                        r.messages.add(m)
                                        print m
                                    else: #existing and new, only load in more recent
                                        r.load_reader_time_series(df[r.column_index][df.index > rts.index[-1]])
                                        r.column_header = column_headers[r.column_index]
                                        r.save()
                                        m = Message(when=timezone.now(),
                                                   message_type='Code Success',
                                                   subject='Updated model.',
                                                   comment='Successfully updated Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                        m.save()
                                        modelinstance.messages.add(m)
                                        r.messages.add(m)
                                        print m
                                except:
                                    m = Message(when=timezone.now(),
                                               message_type='Code Error',
                                               subject='Update model failed.',
                                               comment='Failed to update Reader %s of observed_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                    m.save()
                                    modelinstance.messages.add(m)
                                    print m
                        except:
                            m = Message(when=timezone.now(),
                                       message_type='Code Error',
                                       subject='Update readers failed.',
                                       comment='Failed to update readers of observed_file of model %s of type %s in update_readers function, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                            m.save()
                            modelinstance.messages.add(m)
                            print m
                        else:
                            m = Message(when=timezone.now(),
                                       message_type='Code Success',
                                       subject='Updated readers.',
                                       comment='Successfully updated readers of observed_file of model %s of type %s, except as noted by other code messages.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                            m.save()
                            modelinstance.messages.add(m)
                            print m
            else:
                m = Message(when=timezone.now(),
                            message_type='Code Warning',
                            subject='No observed_file being tracked.',
                            comment='No observed_file being tracked for model %s of type %s, update_readers function moving on.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                m.save()
                modelinstance.messages.add(m)
                print m
            if modelinstance.provided_file_track:
                try:
                    df = pd.read_csv(modelinstance.provided_file.url,
                                     skiprows=modelinstance.provided_file_skiprows,
                                     parse_dates=True,
                                     index_col=modelinstance.provided_file_column_for_indexing)
                except:
                    m = Message(when=timezone.now(),
                               message_type='Code Error',
                               subject='File not found.',
                               comment='Provided_file of model %s of type %s not found by update_readers function, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                    m.save()
                    modelinstance.messages.add(m)
                    print m
                else:
                    try:
                        df = df.sort_index()
                        df.index = df.index - timedelta(hours=float(modelinstance.provided_file_GMT_offset))
                        df.index = df.index.tz_localize('UTC')
                    except:
                        m = Message(when=timezone.now(),
                                   message_type='Code Error',
                                   subject='Adjust dataframe index failed.',
                                   comment='Failed to adjust index of provided_file dataframe of model %s of type %s in update_readers function, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                        m.save()
                        modelinstance.messages.add(m)
                        print m
                    else:
                        column_headers = df.columns
                        df.columns = range(0,len(df.columns))
                        try:
                            for r in modelinstance.readers.filter(source=2).filter(active=True):
                                try:
                                    rts = r.get_reader_time_series()
                                    rts = rts.sort_index()
                                    if len(df[r.column_index]) < 1:  #nothing to add, so abort
                                        m = Message(when=timezone.now(),
                                                   message_type='Code Warning',
                                                   subject='No data.',
                                                   comment='No new data found for Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                        m.save()
                                        modelinstance.messages.add(m)
                                        r.messages.add(m)
                                        print m                                    
                                    elif len(rts) < 1: #nothing existing, so add all new stuff
                                        r.load_reader_time_series(df[r.column_index])
                                        r.column_header = column_headers[r.column_index]
                                        r.save()
                                        m = Message(when=timezone.now(),
                                                   message_type='Code Success',
                                                   subject='Updated model.',
                                                   comment='Successfully updated Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                        m.save()
                                        modelinstance.messages.add(m)
                                        r.messages.add(m)
                                        print m
                                    else: #existing and new, only load in more recent
                                        r.load_reader_time_series(df[r.column_index][df.index > rts.index[-1]])
                                        r.column_header = column_headers[r.column_index]
                                        r.save()
                                        m = Message(when=timezone.now(),
                                                   message_type='Code Success',
                                                   subject='Updated model.',
                                                   comment='Successfully updated Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                        m.save()
                                        modelinstance.messages.add(m)
                                        r.messages.add(m)
                                        print m
                                except:
                                    m = Message(when=timezone.now(),
                                               message_type='Code Error',
                                               subject='Update model failed.',
                                               comment='Failed to update Reader %s of provided_file of model %s of type %s in update_readers function, continuing to next Reader.' % (r.id, modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                                    m.save()
                                    modelinstance.messages.add(m)
                                    print m
                        except:
                            m = Message(when=timezone.now(),
                                       message_type='Code Error',
                                       subject='Update readers failed.',
                                       comment='Failed to update readers of provided_file of model %s of type %s in update_readers function, function aborted.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                            m.save()
                            modelinstance.messages.add(m)
                            print m
                        else:
                            m = Message(when=timezone.now(),
                                       message_type='Code Success',
                                       subject='Updated readers.',
                                       comment='Successfully updated readers of provided_file of model %s of type %s, except as noted by other code messages.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                            m.save()
                            modelinstance.messages.add(m)
                            print m
            else:
                m = Message(when=timezone.now(),
                            message_type='Code Warning',
                            subject='No provided file being tracked.',
                            comment='No provided_file being tracked for model %s of type %s, update_readers function moving on.' % (modelinstance.id, modelinstance.__repr__()[1:].partition(':')[0]))
                m.save()
                modelinstance.messages.add(m)
                print m
    def apportion_savings(self, Tccp=None, Thcp=None):
        """Converts annual savings
        amounts from model into
        monthly amounts based on
        percent cool, heat, and
        flat values."""
        
        try:
            df = pd.DataFrame({'Days': [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]},
                             index = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'])
            if Tccp is None: Tccp = 65.0
            if Thcp is None: Thcp = 65.0
            dd = self.weather_station.get_average_monthly_degree_days(Tccp, Thcp)
            df['CDD'] = dd['CDD']
            df['HDD'] = dd['HDD']
            
            df['flat'] = (df['Days']/365.0).apply(Decimal) * self.percent_flat
            df['cool'] = (df['CDD']/df['CDD'].sum()).apply(Decimal) * self.percent_cool
            df['heat'] = (df['HDD']/df['HDD'].sum()).apply(Decimal) * self.percent_heat
            numlist = [1.0/12.0, 1.0/12.0, 1.0/12.0, 1.0/12.0, 1.0/12.0, 1.0/12.0, 
                                     1.0/12.0, 1.0/12.0, 1.0/12.0, 1.0/12.0, 1.0/12.0, 1.0/12.0]
            df['fixed'] = pd.Series([float(self.percent_fixed) * x for x in numlist], index=df.index).apply(Decimal)
            
            self.jan_cons = (df['flat'][0] + df['heat'][0] + df['cool'][0] + df['fixed'][0]) * self.annual_consumption_savings
            self.feb_cons = (df['flat'][1] + df['heat'][1] + df['cool'][1] + df['fixed'][1]) * self.annual_consumption_savings
            self.mar_cons = (df['flat'][2] + df['heat'][2] + df['cool'][2] + df['fixed'][2]) * self.annual_consumption_savings
            self.apr_cons = (df['flat'][3] + df['heat'][3] + df['cool'][3] + df['fixed'][3]) * self.annual_consumption_savings
            self.may_cons = (df['flat'][4] + df['heat'][4] + df['cool'][4] + df['fixed'][4]) * self.annual_consumption_savings
            self.jun_cons = (df['flat'][5] + df['heat'][5] + df['cool'][5] + df['fixed'][5]) * self.annual_consumption_savings
            self.jul_cons = (df['flat'][6] + df['heat'][6] + df['cool'][6] + df['fixed'][6]) * self.annual_consumption_savings
            self.aug_cons = (df['flat'][7] + df['heat'][7] + df['cool'][7] + df['fixed'][7]) * self.annual_consumption_savings
            self.sep_cons = (df['flat'][8] + df['heat'][8] + df['cool'][8] + df['fixed'][8]) * self.annual_consumption_savings
            self.oct_cons = (df['flat'][9] + df['heat'][9] + df['cool'][9] + df['fixed'][9]) * self.annual_consumption_savings
            self.nov_cons = (df['flat'][10] + df['heat'][10] + df['cool'][10] + df['fixed'][10]) * self.annual_consumption_savings
            self.dec_cons = (df['flat'][11] + df['heat'][11] + df['cool'][11] + df['fixed'][11]) * self.annual_consumption_savings

            self.jan_peak = (df['flat'][0] + df['heat'][0] + df['cool'][0] + df['fixed'][0]) * self.peak_demand_savings
            self.feb_peak = (df['flat'][1] + df['heat'][1] + df['cool'][1] + df['fixed'][1]) * self.peak_demand_savings
            self.mar_peak = (df['flat'][2] + df['heat'][2] + df['cool'][2] + df['fixed'][2]) * self.peak_demand_savings
            self.apr_peak = (df['flat'][3] + df['heat'][3] + df['cool'][3] + df['fixed'][3]) * self.peak_demand_savings
            self.may_peak = (df['flat'][4] + df['heat'][4] + df['cool'][4] + df['fixed'][4]) * self.peak_demand_savings
            self.jun_peak = (df['flat'][5] + df['heat'][5] + df['cool'][5] + df['fixed'][5]) * self.peak_demand_savings
            self.jul_peak = (df['flat'][6] + df['heat'][6] + df['cool'][6] + df['fixed'][6]) * self.peak_demand_savings
            self.aug_peak = (df['flat'][7] + df['heat'][7] + df['cool'][7] + df['fixed'][7]) * self.peak_demand_savings
            self.sep_peak = (df['flat'][8] + df['heat'][8] + df['cool'][8] + df['fixed'][8]) * self.peak_demand_savings
            self.oct_peak = (df['flat'][9] + df['heat'][9] + df['cool'][9] + df['fixed'][9]) * self.peak_demand_savings
            self.nov_peak = (df['flat'][10] + df['heat'][10] + df['cool'][10] + df['fixed'][10]) * self.peak_demand_savings
            self.dec_peak = (df['flat'][11] + df['heat'][11] + df['cool'][11] + df['fixed'][11]) * self.peak_demand_savings

            self.jan_cost = (df['flat'][0] + df['heat'][0] + df['cool'][0] + df['fixed'][0]) * self.annual_cost_savings
            self.feb_cost = (df['flat'][1] + df['heat'][1] + df['cool'][1] + df['fixed'][1]) * self.annual_cost_savings
            self.mar_cost = (df['flat'][2] + df['heat'][2] + df['cool'][2] + df['fixed'][2]) * self.annual_cost_savings
            self.apr_cost = (df['flat'][3] + df['heat'][3] + df['cool'][3] + df['fixed'][3]) * self.annual_cost_savings
            self.may_cost = (df['flat'][4] + df['heat'][4] + df['cool'][4] + df['fixed'][4]) * self.annual_cost_savings
            self.jun_cost = (df['flat'][5] + df['heat'][5] + df['cool'][5] + df['fixed'][5]) * self.annual_cost_savings
            self.jul_cost = (df['flat'][6] + df['heat'][6] + df['cool'][6] + df['fixed'][6]) * self.annual_cost_savings
            self.aug_cost = (df['flat'][7] + df['heat'][7] + df['cool'][7] + df['fixed'][7]) * self.annual_cost_savings
            self.sep_cost = (df['flat'][8] + df['heat'][8] + df['cool'][8] + df['fixed'][8]) * self.annual_cost_savings
            self.oct_cost = (df['flat'][9] + df['heat'][9] + df['cool'][9] + df['fixed'][9]) * self.annual_cost_savings
            self.nov_cost = (df['flat'][10] + df['heat'][10] + df['cool'][10] + df['fixed'][10]) * self.annual_cost_savings
            self.dec_cost = (df['flat'][11] + df['heat'][11] + df['cool'][11] + df['fixed'][11]) * self.annual_cost_savings
        except:
            m = Message(when=timezone.now(),
                        message_type='Code Error',
                        subject='Calculation failed.',
                        comment='EfficiencyMeasure %s apportion_savings failed, function aborted.' % self.id)
            m.save()
            self.messages.add(m)
            print m
        else:
            self.save()
    def get_cost_df(self, df, billx=None):
        """function(df, billx=None)
        
        Given dataframe with monthly
        index and Peak Demand and
        Consumption, along with a
        BILLx Monther whose data
        will be retrieved for use
        in calculating billing
        demand from historical data,
        returns dataframe with
        Calculated Cost and Billing
        Demand columns."""
        try:
            if self.use_input_billing_demand and ('Billing Demand' not in df.columns): raise TypeError
            if self.use_input_billing_demand:
                if float('NaN') not in df['Billing Demand']:
                    df['Calculated Billing Demand'] = df['Billing Demand'].copy()  #if using input, this is 1st choice
                else:
                    df['Calculated Billing Demand'] = df['Peak Demand'].copy()     #if NaNs, then this is 2nd choice
            else:
                df = self.get_billing_demand_df(df = df.copy(), billx = billx)     #if not using input, must calculate it with historical data from billx
            if 'Calculated Billing Demand' not in df.columns: raise TypeError
            df['Billing Demand'] = df['Calculated Billing Demand'].copy()

#            df['Billing Demand'] = df['Billing Demand'].apply(Decimal)
#            df['Peak Demand'] = df['Peak Demand'].apply(Decimal)
#            df['Consumption'] = df['Consumption'].apply(Decimal)
        except:
            m = Message(when=timezone.now(),
                    message_type='Code Error',
                    subject='Calculation failed.',
                    comment='GAPowerPandL %s get_cost_df unable to calculate billing demand, function aborted.' % self.id)
            m.save()
            self.messages.add(m)
            print m
        else:
            try:
                df['k1'] = [min(df['Billing Demand'][i]*float(self.tier1),df['Consumption'][i]) for i in range(0,len(df))]
                df['k2'] = [min(df['Billing Demand'][i]*(float(self.tier2)-float(self.tier1)),df['Consumption'][i]-df['k1'][i]) for i in range(0,len(df))]
                df['k3'] = [min(df['Billing Demand'][i]*(float(self.tier3)-float(self.tier2)),df['Consumption'][i]-df['k1'][i]-df['k2'][i]) for i in range(0,len(df))]
                df['k4'] = [min(df['Billing Demand'][i]*(float(self.tier4)-float(self.tier3)),df['Consumption'][i]-df['k1'][i]-df['k2'][i]-df['k3'][i]) for i in range(0,len(df))]
                
                df['k1a'] = [min(df['k1'][i],float(self.tier1a)) for i in range(0,len(df))]
                df['k1b'] = [min(df['k1'][i]-df['k1a'][i],float(self.tier1b)-float(self.tier1a)) for i in range(0,len(df))]
                df['k1c'] = [min(df['k1'][i]-df['k1a'][i]-df['k1b'][i],float(self.tier1c)-float(self.tier1b)) for i in range(0,len(df))]
                df['k1d'] = [min(df['k1'][i]-df['k1a'][i]-df['k1b'][i]-df['k1c'][i],float(self.tier1d)-float(self.tier1c)) for i in range(0,len(df))]
                
                df['k2a'] = [min(df['k2'][i],float(self.tier2a)) for i in range(0,len(df))]
                df['k2b'] = [min(df['k2'][i]-df['k2a'][i],float(self.tier2b)-float(self.tier2a)) for i in range(0,len(df))]
                df['k2c'] = [min(df['k2'][i]-df['k2a'][i]-df['k2b'][i],float(self.tier2c)-float(self.tier2b)) for i in range(0,len(df))]
                df['k2d'] = [min(df['k2'][i]-df['k2a'][i]-df['k2b'][i]-df['k2c'][i],float(self.tier2d)-float(self.tier2c)) for i in range(0,len(df))]
                
                df['k3a'] = [min(df['k3'][i],float(self.tier3a)) for i in range(0,len(df))]
                df['k3b'] = [min(df['k3'][i]-df['k3a'][i],float(self.tier3b)-float(self.tier3a)) for i in range(0,len(df))]
                df['k3c'] = [min(df['k3'][i]-df['k3a'][i]-df['k3b'][i],float(self.tier3c)-float(self.tier3b)) for i in range(0,len(df))]
                df['k3d'] = [min(df['k3'][i]-df['k3a'][i]-df['k3b'][i]-df['k3c'][i],float(self.tier3d)-float(self.tier3c)) for i in range(0,len(df))]
                
                df['k4a'] = [min(df['k4'][i],float(self.tier4a)) for i in range(0,len(df))]
                df['k4b'] = [min(df['k4'][i]-df['k4a'][i],float(self.tier4b)-float(self.tier4a)) for i in range(0,len(df))]
                df['k4c'] = [min(df['k4'][i]-df['k4a'][i]-df['k4b'][i],float(self.tier4c)-float(self.tier4b)) for i in range(0,len(df))]
                df['k4d'] = [min(df['k4'][i]-df['k4a'][i]-df['k4b'][i]-df['k4c'][i],float(self.tier4d)-float(self.tier4c)) for i in range(0,len(df))]
                
                df['checksum'] = (  df['k1a'] + df['k1b'] + df['k1c'] + df['k1d'] +
                                    df['k2a'] + df['k2b'] + df['k2c'] + df['k2d'] +
                                    df['k3a'] + df['k3b'] + df['k3c'] + df['k3d'] +
                                    df['k4a'] + df['k4b'] + df['k4c'] + df['k4d']   )
                sum1 = df['checksum'].sum()
                sum2 = df['Consumption'].sum()
                
                if not(sum1 == float(0) and sum2 == float(0)):
                    if min(sum1,sum2)/max(sum1,sum2) < float(0.999):
                        m = Message(when=timezone.now(),
                                message_type='Code Error',
                                subject='Calculation failed.',
                                comment='GAPowerPandL %s get_cost_df computed tier consumptions that do not sum to total consumption, function aborted.' % self.id)
                        m.save()
                        self.messages.add(m)
                        print m
                        raise TypeError
                
                df['Consumption Cost'] = (  df['k1a']*float(self.rate1a) + df['k1b']*float(self.rate1b) +
                                            df['k1c']*float(self.rate1c) + df['k1d']*float(self.rate1d) +
                                            df['k2a']*float(self.rate2a) + df['k2b']*float(self.rate2b) +
                                            df['k2c']*float(self.rate2c) + df['k2d']*float(self.rate2d) +
                                            df['k3a']*float(self.rate3a) + df['k3b']*float(self.rate3b) +
                                            df['k3c']*float(self.rate3c) + df['k3d']*float(self.rate3d) +
                                            df['k4a']*float(self.rate4a) + df['k4b']*float(self.rate4b) +
                                            df['k4c']*float(self.rate4c) + df['k4d']*float(self.rate4d)   )
                
            except:
                m = Message(when=timezone.now(),
                        message_type='Code Warning',
                        subject='Calculation failed.',
                        comment='GAPowerPandL %s get_cost_df unable to calculate consumption charges, function aborted.' % self.id)
                m.save()
                self.messages.add(m)
                print m
            else:
                try:
                    df['Excess Demand'] = float(0.0)
                    df['Excess Demand Cost'] = float(0.0)
                    #--seems the straight per kW charge is only for calculating a minimum bill
                    #--thus the following two lines don't belong here but would be used in a 
                    #----different function to compare the default calculated cost against
                    #----the minimum monthly bill from paragraph A. of the rate schedule
                    #df['Excess Demand'] = [max(df['Calculated Billing Demand'][i] - self.excess_kW_threshold, float(0.0)) for i in range(0,len(df))]
                    #df['Excess Demand Cost'] = df['Excess Demand'] * self.excess_kW_rate
                except:
                    m = Message(when=timezone.now(),
                            message_type='Code Error',
                            subject='Calculation failed.',
                            comment='GAPowerPandL %s get_cost_df unable to calculate excess demand charges, function aborted.' % self.id)
                    m.save()
                    self.messages.add(m)
                    print m
                else:
                    try:
                        df['Basic Service Cost'] = float(self.basic_service_charge)
                    except:
                        m = Message(when=timezone.now(),
                                message_type='Code Error',
                                subject='Calculation failed.',
                                comment='GAPowerPandL %s get_cost_df unable to calculate base charges, function aborted.' % self.id)
                        m.save()
                        self.messages.add(m)
                        print m
                    else:
                        try:
                            df['Base Revenue Cost'] = df['Consumption Cost'] + df['Basic Service Cost']
                            df['Base Revenue Riders Cost'] = float(0.0)
                            for base_rider in self.riders.all(): #can't use filter because of child class usage
                                if base_rider.as_child().apply_to_base_revenue or base_rider.as_child().apply_to_consumption: #must instead check here to pull only base rev riders first
                                    df[base_rider.as_child().name] = base_rider.as_child().get_cost_df(df)['Rider Cost']
                                    df['Base Revenue Riders Cost'] = df['Base Revenue Riders Cost'] + base_rider.as_child().get_cost_df(df)['Rider Cost']
                            df['Total Revenue Cost'] = df['Base Revenue Cost'] + df['Base Revenue Riders Cost']
                            df['Total Revenue Riders Cost'] = float(0.0)
                            for total_rider in self.riders.all(): #can't use filter because of child class usage
                                if total_rider.as_child().apply_to_total_revenue: #must instead check here to pull only total rev riders
                                    df[total_rider.as_child().name] = total_rider.as_child().get_cost_df(df)['Rider Cost']
                                    df['Total Revenue Riders Cost'] = df['Total Revenue Riders Cost'] + total_rider.as_child().get_cost_df(df)['Rider Cost']
                        except:
                            m = Message(when=timezone.now(),
                                    message_type='Code Error',
                                    subject='Calculation failed.',
                                    comment='GAPowerPandL %s get_cost_df unable to calculate rider charges, function aborted.' % self.id)
                            m.save()
                            self.messages.add(m)
                            print m
                        else:
                            df['Calculated Cost'] = (  df['Basic Service Cost'] + df['Excess Demand Cost'] +
                                                       df['Consumption Cost'] + df['Base Revenue Riders Cost'] +
                                                       df['Total Revenue Riders Cost'] 
                                                       ) * (float(1.0) + float(self.tax_percentage))
        return df
    def get_monther_period_dataframe(self,  first_month='', last_month=''):
        """Optional inputs:
              first_month
              last_month
                  as 'mm/yyyy'
        
        Returns Monther's dataframe."""
        t0 = timezone.now()
        try:
            mlg_set = self.monthling_set.all()
            if mlg_set.count() == 0:
                m = Message(when=timezone.now(),
                            message_type='Code Warning',
                            subject='Nothing to return.',
                            comment='Monther %s, get_monther_period_dataframe called when no Monthlings present.' % self.id)
                m.save()
                self.messages.add(m)
                print m
                df = None
            else:
                min_month = pd.Period(mlg_set.aggregate(Min('when'))['when__min'], freq='M')
                max_month = pd.Period(mlg_set.aggregate(Max('when'))['when__max'], freq='M')
                if first_month == '':
                    first_month = pd.Period(min_month, freq='M')
                else:
                    first_month = pd.Period(first_month, freq='M')
                if last_month == '':
                    last_month = pd.Period(max_month, freq='M')
                else:
                    last_month = pd.Period(last_month, freq='M')
                
                first_month_n = first_month.year + first_month.month/100.0
                last_month_n = last_month.year + last_month.month/100.0
                min_month_n = min_month.year + min_month.month/100.0
                max_month_n = max_month.year + max_month.month/100.0
                if last_month_n < first_month_n: #then switch them
                    first_month,first_month_n,last_month,last_month_n = last_month,last_month_n,first_month,first_month_n
        except:
            m = Message(when=timezone.now(),
                          message_type='Code Error',
                          subject='Unable to calculate range.',
                          comment='Monther %s get_monther_period_dataframe failed to determine data range.' % self.id)
            m.save()
            self.messages.add(m)
            print m
            df = None
        else:
            try: #here we give as much data in the requested range as we have, unless there isn't any
                if (last_month_n < min_month_n) or (first_month_n > max_month_n): raise ValueError
                elif (max_month_n >= last_month_n >= min_month_n) and (first_month_n < min_month_n):
                    first_month = min_month
                elif (max_month_n >= last_month_n >= min_month_n) and (min_month_n <= first_month_n <= max_month_n):
                    pass
                elif (last_month_n > max_month_n) and (min_month_n <= first_month_n <= max_month_n):
                    last_month = max_month
                elif (first_month_n <= min_month_n) and (last_month_n >= max_month_n):
                    first_month = min_month
                    last_month = max_month
            except:
                m = Message(when=timezone.now(),
                              message_type='Code Warning',
                              subject='Requested data range does not exist.',
                              comment='Monther %s get_monther_period_dataframe given range where no data exists.' % self.id)
                m.save()
                self.messages.add(m)
                print m
                df = None
            else:
                try:
                    t1 = (first_month.to_timestamp()+timedelta(days=10,hours=11,minutes=11,seconds=11)).tz_localize(UTC)
                    t2 = (last_month.to_timestamp()+timedelta(days=10,hours=11,minutes=11,seconds=11)).tz_localize(UTC)

                    mlg_set_filtered = mlg_set.filter(when__gte=t1).filter(when__lte=t2).order_by('when')

                    t = [x.when for x in mlg_set_filtered]
                    updated = pd.Series([x.updated for x in mlg_set_filtered],index=t)
                    start_date = pd.Series([x.start_date for x in mlg_set_filtered],index=t)
                    end_date = pd.Series([x.end_date for x in mlg_set_filtered],index=t)
                    
                    cdd_peak_demand = pd.Series([float(x.cdd_peak_demand) for x in mlg_set_filtered],index=t)
                    hdd_peak_demand = pd.Series([float(x.hdd_peak_demand) for x in mlg_set_filtered],index=t)
                    cdd_consumption = pd.Series([float(x.cdd_consumption) for x in mlg_set_filtered],index=t)
                    hdd_consumption = pd.Series([float(x.hdd_consumption) for x in mlg_set_filtered],index=t)
        
                    base_bdm = pd.Series([float(x.base_billing_demand) for x in mlg_set_filtered],index=t)
                    base_pdm = pd.Series([float(x.base_peak_demand) for x in mlg_set_filtered],index=t)
                    base_con = pd.Series([float(x.base_consumption) for x in mlg_set_filtered],index=t)
                    base_kpd = pd.Series([float(x.base_kBtuh_peak_demand) for x in mlg_set_filtered],index=t)
                    base_kco = pd.Series([float(x.base_kBtu_consumption) for x in mlg_set_filtered],index=t)
                    base_dol = pd.Series([float(x.base_cost) for x in mlg_set_filtered],index=t)
        
                    exp_bdm = pd.Series([float(x.exp_billing_demand) for x in mlg_set_filtered],index=t)
                    exp_pdm = pd.Series([float(x.exp_peak_demand) for x in mlg_set_filtered],index=t)
                    exp_con = pd.Series([float(x.exp_consumption) for x in mlg_set_filtered],index=t)
                    exp_kpd = pd.Series([float(x.exp_kBtuh_peak_demand) for x in mlg_set_filtered],index=t)
                    exp_kco = pd.Series([float(x.exp_kBtu_consumption) for x in mlg_set_filtered],index=t)
                    exp_dol = pd.Series([float(x.exp_cost) for x in mlg_set_filtered],index=t)
        
                    act_bdm = pd.Series([float(x.act_billing_demand) for x in mlg_set_filtered],index=t)
                    act_pdm = pd.Series([float(x.act_peak_demand) for x in mlg_set_filtered],index=t)
                    act_con = pd.Series([float(x.act_consumption) for x in mlg_set_filtered],index=t)
                    act_kpd = pd.Series([float(x.act_kBtuh_peak_demand) for x in mlg_set_filtered],index=t)
                    act_kco = pd.Series([float(x.act_kBtu_consumption) for x in mlg_set_filtered],index=t)
                    act_dol = pd.Series([float(x.act_cost) for x in mlg_set_filtered],index=t)
        
                    esave_bdm = pd.Series([float(x.esave_billing_demand) for x in mlg_set_filtered],index=t)
                    esave_pdm = pd.Series([float(x.esave_peak_demand) for x in mlg_set_filtered],index=t)
                    esave_con = pd.Series([float(x.esave_consumption) for x in mlg_set_filtered],index=t)
                    esave_kpd = pd.Series([float(x.esave_kBtuh_peak_demand) for x in mlg_set_filtered],index=t)
                    esave_kco = pd.Series([float(x.esave_kBtu_consumption) for x in mlg_set_filtered],index=t)
                    esave_dol = pd.Series([float(x.esave_cost) for x in mlg_set_filtered],index=t)
        
                    asave_bdm = pd.Series([float(x.asave_billing_demand) for x in mlg_set_filtered],index=t)
                    asave_pdm = pd.Series([float(x.asave_peak_demand) for x in mlg_set_filtered],index=t)
                    asave_con = pd.Series([float(x.asave_consumption) for x in mlg_set_filtered],index=t)
                    asave_kpd = pd.Series([float(x.asave_kBtuh_peak_demand) for x in mlg_set_filtered],index=t)
                    asave_kco = pd.Series([float(x.asave_kBtu_consumption) for x in mlg_set_filtered],index=t)
                    asave_dol = pd.Series([float(x.asave_cost) for x in mlg_set_filtered],index=t)
        
                    base_bdm_d = pd.Series([float(x.base_billing_demand_delta) for x in mlg_set_filtered],index=t)
                    base_pdm_d = pd.Series([float(x.base_peak_demand_delta) for x in mlg_set_filtered],index=t)
                    base_con_d = pd.Series([float(x.base_consumption_delta) for x in mlg_set_filtered],index=t)
                    base_kpd_d = pd.Series([float(x.base_kBtuh_peak_demand_delta) for x in mlg_set_filtered],index=t)
                    base_kco_d = pd.Series([float(x.base_kBtu_consumption_delta) for x in mlg_set_filtered],index=t)
                    base_dol_d = pd.Series([float(x.base_cost_delta) for x in mlg_set_filtered],index=t)
        
                    exp_bdm_d = pd.Series([float(x.exp_billing_demand_delta) for x in mlg_set_filtered],index=t)
                    exp_pdm_d = pd.Series([float(x.exp_peak_demand_delta) for x in mlg_set_filtered],index=t)
                    exp_con_d = pd.Series([float(x.exp_consumption_delta) for x in mlg_set_filtered],index=t)
                    exp_kpd_d = pd.Series([float(x.exp_kBtuh_peak_demand_delta) for x in mlg_set_filtered],index=t)
                    exp_kco_d = pd.Series([float(x.exp_kBtu_consumption_delta) for x in mlg_set_filtered],index=t)
                    exp_dol_d = pd.Series([float(x.exp_cost_delta) for x in mlg_set_filtered],index=t)
        
                    esave_bdm_d = pd.Series([float(x.esave_billing_demand_delta) for x in mlg_set_filtered],index=t)
                    esave_pdm_d = pd.Series([float(x.esave_peak_demand_delta) for x in mlg_set_filtered],index=t)
                    esave_con_d = pd.Series([float(x.esave_consumption_delta) for x in mlg_set_filtered],index=t)
                    esave_kpd_d = pd.Series([float(x.esave_kBtuh_peak_demand_delta) for x in mlg_set_filtered],index=t)
                    esave_kco_d = pd.Series([float(x.esave_kBtu_consumption_delta) for x in mlg_set_filtered],index=t)
                    esave_dol_d = pd.Series([float(x.esave_cost_delta) for x in mlg_set_filtered],index=t)
    
                    X_a_cons = pd.Series([float(x.X_a_cons) for x in mlg_set_filtered],index=t)
                    X_b_cons = pd.Series([float(x.X_b_cons) for x in mlg_set_filtered],index=t)
                    X_a_pd = pd.Series([float(x.X_a_pd) for x in mlg_set_filtered],index=t)
                    X_b_pd = pd.Series([float(x.X_b_pd) for x in mlg_set_filtered],index=t)

                    df = pd.DataFrame({
                                        'Updated' : updated,

                                        'Start Date' : start_date,
                                        'End Date' : end_date,
                                        
                                        'CDD (peak demand)' : cdd_peak_demand,
                                        'HDD (peak demand)' : hdd_peak_demand,
                                        'CDD (consumption)' : cdd_consumption,
                                        'HDD (consumption)' : hdd_consumption,
        
                                        'Billing Demand (base)' : base_bdm,
                                        'Peak Demand (base)' : base_pdm,
                                        'Consumption (base)' : base_con,
                                        'kBtuh Peak Demand (base)' : base_kpd,
                                        'kBtu Consumption (base)' : base_kco,
                                        'Cost (base)' : base_dol,
        
                                        'Billing Demand (exp)' : exp_bdm,
                                        'Peak Demand (exp)' : exp_pdm,
                                        'Consumption (exp)' : exp_con,
                                        'kBtuh Peak Demand (exp)' : exp_kpd,
                                        'kBtu Consumption (exp)' : exp_kco,
                                        'Cost (exp)' : exp_dol,
        
                                        'Billing Demand (act)' : act_bdm,
                                        'Peak Demand (act)' : act_pdm,
                                        'Consumption (act)' : act_con,
                                        'kBtuh Peak Demand (act)' : act_kpd,
                                        'kBtu Consumption (act)' : act_kco,
                                        'Cost (act)' : act_dol,
        
                                        'Billing Demand (esave)' : esave_bdm,
                                        'Peak Demand (esave)' : esave_pdm,
                                        'Consumption (esave)' : esave_con,
                                        'kBtuh Peak Demand (esave)' : esave_kpd,
                                        'kBtu Consumption (esave)' : esave_kco,
                                        'Cost (esave)' : esave_dol,
        
                                        'Billing Demand (asave)' : asave_bdm,
                                        'Peak Demand (asave)' : asave_pdm,
                                        'Consumption (asave)' : asave_con,
                                        'kBtuh Peak Demand (asave)' : asave_kpd,
                                        'kBtu Consumption (asave)' : asave_kco,
                                        'Cost (asave)' : asave_dol,
        
                                        'Billing Demand (base delta)' : base_bdm_d,
                                        'Peak Demand (base delta)' : base_pdm_d,
                                        'Consumption (base delta)' : base_con_d,
                                        'kBtuh Peak Demand (base delta)' : base_kpd_d,
                                        'kBtu Consumption (base delta)' : base_kco_d,
                                        'Cost (base delta)' : base_dol_d,
        
                                        'Billing Demand (exp delta)' : exp_bdm_d,
                                        'Peak Demand (exp delta)' : exp_pdm_d,
                                        'Consumption (exp delta)' : exp_con_d,
                                        'kBtuh Peak Demand (exp delta)' : exp_kpd_d,
                                        'kBtu Consumption (exp delta)' : exp_kco_d,
                                        'Cost (exp delta)' : exp_dol_d,
        
                                        'Billing Demand (esave delta)' : esave_bdm_d,
                                        'Peak Demand (esave delta)' : esave_pdm_d,
                                        'Consumption (esave delta)' : esave_con_d,
                                        'kBtuh Peak Demand (esave delta)' : esave_kpd_d,
                                        'kBtu Consumption (esave delta)' : esave_kco_d,
                                        'Cost (esave delta)' : esave_dol_d,

                                        'X_a_cons' : X_a_cons,
                                        'X_b_cons' : X_b_cons,
                                        'X_a_pd' : X_a_pd,
                                        'X_b_pd' : X_b_pd,
                                        })
                    df.index = df.index.to_period(freq='M')
                    df = df.sort_index()
                except:
                    m = Message(when=timezone.now(),
                                  message_type='Code Error',
                                  subject='Unable to retrieve data.',
                                  comment='Monther %s get_monther_period_dataframe unable to retrieve data.' % self.id)
                    m.save()
                    self.messages.add(m)
                    print m
                    df = None
        t1 = timezone.now()
        logger.debug('get_monther_period_dataframe %s' % '{0:,.0f}'.format((t1-t0).seconds*1000.0 + (t1-t0).microseconds/1000.0))
        return df
    def save(self, *args, **kwargs):
        if self.id is None:
            super(PackageUnit, self).save(*args, **kwargs)
            tnow = timezone.now()
            m = Message(when = tnow,
                        message_type = 'Model Info',
                        subject = 'Model created.',
                        comment = 'This Package Unit was created on %s.' % tnow)
            m.save()
            self.messages.add(m)
            
            #----------------------------------------------------------------observed
            #states
            s = Reader(name='SADBo',help_text='Package Unit: supply air dry bulb temperature (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SARHo',help_text='Package Unit: supply air relative humidity (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SAFLo',help_text='Package Unit: supply air flow rate (CFM) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SADPo',help_text='Package Unit: supply air pressure (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='RADBo',help_text='Package Unit: return air dry bulb temperature (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='RARHo',help_text='Package Unit: return air relative humidity (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='RAFLo',help_text='Package Unit: return air flow rate (CFM) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='RADPo',help_text='Package Unit: return air pressure (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='OADBo',help_text='Package Unit: outside air dry bulb temperature (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='OARHo',help_text='Package Unit: outside air relative humidity (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='OAFLo',help_text='Package Unit: outside air flow rate (CFM) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='OADPo',help_text='Package Unit: outside air pressure (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='MADBo',help_text='Package Unit: mixed air dry bulb temperature (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='MARHo',help_text='Package Unit: mixed air relative humidity (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='MAFLo',help_text='Package Unit: mixed air flow rate (CFM) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='MADPo',help_text='Package Unit: mixed air pressure (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPDBo',help_text='Package Unit: space dry bulb temperature (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPRHo',help_text='Package Unit: space relative humidity (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPDPo',help_text='Package Unit: space air pressure (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='FIDPo',help_text='Package Unit: filter pressure drop (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='CCDPo',help_text='Package Unit: cooling coil pressure drop (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SFDPo',help_text='Package Unit: supply fan pressure gain (iwg) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SFEFo',help_text='Package Unit: supply fan efficiency (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SMEFo',help_text='Package Unit: supply fan motor efficiency (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SVFDo',help_text='Package Unit: supply fan VFD efficiency (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='RFCHo',help_text='Package Unit: refrigerant charge (lbs) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='TONSo',help_text='Package Unit: delivered cooling (tons) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='BTUHo',help_text='Package Unit: delivered heating (btu/h) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='CEERo',help_text='Package Unit: cooling efficiency (EER) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='HEFFo',help_text='Package Unit: heating efficiency (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='VFDNo',help_text='Package Unit: VFD speed ratio {n} (0-1.0) observed by sensors')
            s.save()
            self.readers.add(s)

            #setpoint arrays
            s = Reader(name='SCOCo',help_text='Package Unit: occupied cooling setpoints (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SCUNo',help_text='Package Unit: unoccupied cooling setpoints (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SHOCo',help_text='Package Unit: occupied heating setpoints (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SHUNo',help_text='Package Unit: unoccupied heating setpoints (' + u"\u00b0" + 'F) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='SRFCo',help_text='Package Unit: fully charged refrigerant pressures (lbs) observed by sensors')
            s.save()
            self.readers.add(s)

            #status
            s = Reader(name='STONo',help_text='Package Unit: unit status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC1o',help_text='Package Unit: compressor 1 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC2o',help_text='Package Unit: compressor 2 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC3o',help_text='Package Unit: compressor 3 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STH1o',help_text='Package Unit: heating 1 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STH2o',help_text='Package Unit: heating 2 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STECo',help_text='Package Unit: economizer status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STODo',help_text='Package Unit: outdoor air damper status (% open) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STSFo',help_text='Package Unit: supply fan status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STRFo',help_text='Package Unit: return fan status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STF1o',help_text='Package Unit: condenser fan 1 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STF2o',help_text='Package Unit: condenser fan 2 status (1/0) observed by sensors')
            s.save()
            self.readers.add(s)
            #----------------------------------------------------------------provided
            #states
            s = Reader(name='SADBp',help_text='Package Unit: supply air dry bulb temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SARHp',help_text='Package Unit: supply air relative humidity (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SAFLp',help_text='Package Unit: supply air flow rate (CFM) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SADPp',help_text='Package Unit: supply air pressure (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='RADBp',help_text='Package Unit: return air dry bulb temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='RARHp',help_text='Package Unit: return air relative humidity (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='RAFLp',help_text='Package Unit: return air flow rate (CFM) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='RADPp',help_text='Package Unit: return air pressure (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='OADBp',help_text='Package Unit: outside air dry bulb temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='OARHp',help_text='Package Unit: outside air relative humidity (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='OAFLp',help_text='Package Unit: outside air flow rate (CFM) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='OADPp',help_text='Package Unit: outside air pressure (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='MADBp',help_text='Package Unit: mixed air dry bulb temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='MARHp',help_text='Package Unit: mixed air relative humidity (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='MAFLp',help_text='Package Unit: mixed air flow rate (CFM) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='MADPp',help_text='Package Unit: mixed air pressure (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPDBp',help_text='Package Unit: space dry bulb temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPRHp',help_text='Package Unit: space relative humidity (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPDPp',help_text='Package Unit: space air pressure (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='FIDPp',help_text='Package Unit: filter pressure drop (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='CCDPp',help_text='Package Unit: cooling coil pressure drop (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SFDPp',help_text='Package Unit: supply fan pressure gain (iwg) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SFEFp',help_text='Package Unit: supply fan efficiency (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SMEFp',help_text='Package Unit: supply fan motor efficiency (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SVFDp',help_text='Package Unit: supply fan VFD efficiency (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='RFCHp',help_text='Package Unit: refrigerant charge (lbs) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='TONSp',help_text='Package Unit: delivered cooling (tons) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='BTUHp',help_text='Package Unit: delivered heating (btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='CEERp',help_text='Package Unit: cooling efficiency (EER) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='HEFFp',help_text='Package Unit: heating efficiency (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='VFDNp',help_text='Package Unit: VFD speed ratio {n} (0-1.0) provided by remote source')
            s.save()
            self.readers.add(s)

            #setpoint arrays
            s = Reader(name='SCOCp',help_text='Package Unit: occupied cooling setpoints (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SCUNp',help_text='Package Unit: unoccupied cooling setpoints (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SHOCp',help_text='Package Unit: occupied heating setpoints (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SHUNp',help_text='Package Unit: unoccupied heating setpoints (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='SRFCp',help_text='Package Unit: fully charged refrigerant pressures (lbs) provided by remote source')
            s.save()
            self.readers.add(s)

            #status
            s = Reader(name='STONp',help_text='Package Unit: unit status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC1p',help_text='Package Unit: compressor 1 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC2p',help_text='Package Unit: compressor 2 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC3p',help_text='Package Unit: compressor 3 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STH1p',help_text='Package Unit: heating 1 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STH2p',help_text='Package Unit: heating 2 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STECp',help_text='Package Unit: economizer status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STODp',help_text='Package Unit: outdoor air damper status (% open) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STSFp',help_text='Package Unit: supply fan status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STRFp',help_text='Package Unit: return fan status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STF1p',help_text='Package Unit: condenser fan 1 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STF2p',help_text='Package Unit: condenser fan 2 status (1/0) provided by remote source')
            s.save()
            self.readers.add(s)
            #----------------------------------------------------------------calculated
            #states
            s = Reader(name='SADBc',help_text='Package Unit: supply air dry bulb temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SARHc',help_text='Package Unit: supply air relative humidity (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SAFLc',help_text='Package Unit: supply air flow rate (CFM) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SADPc',help_text='Package Unit: supply air pressure (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='RADBc',help_text='Package Unit: return air dry bulb temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='RARHc',help_text='Package Unit: return air relative humidity (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='RAFLc',help_text='Package Unit: return air flow rate (CFM) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='RADPc',help_text='Package Unit: return air pressure (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='OADBc',help_text='Package Unit: outside air dry bulb temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='OARHc',help_text='Package Unit: outside air relative humidity (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='OAFLc',help_text='Package Unit: outside air flow rate (CFM) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='OADPc',help_text='Package Unit: outside air pressure (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='MADBc',help_text='Package Unit: mixed air dry bulb temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='MARHc',help_text='Package Unit: mixed air relative humidity (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='MAFLc',help_text='Package Unit: mixed air flow rate (CFM) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='MADPc',help_text='Package Unit: mixed air pressure (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPDBc',help_text='Package Unit: space dry bulb temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPRHc',help_text='Package Unit: space relative humidity (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SPDPc',help_text='Package Unit: space air pressure (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='FIDPc',help_text='Package Unit: filter pressure drop (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='CCDPc',help_text='Package Unit: cooling coil pressure drop (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SFDPc',help_text='Package Unit: supply fan pressure gain (iwg) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SFEFc',help_text='Package Unit: supply fan efficiency (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SMEFc',help_text='Package Unit: supply fan motor efficiency (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SVFDc',help_text='Package Unit: supply fan VFD efficiency (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='RFCHc',help_text='Package Unit: refrigerant charge (lbs) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='TONSc',help_text='Package Unit: delivered cooling (tons) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='BTUHc',help_text='Package Unit: delivered heating (btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='CEERc',help_text='Package Unit: cooling efficiency (EER) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='HEFFc',help_text='Package Unit: heating efficiency (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='VFDNc',help_text='Package Unit: VFD speed ratio {n} (0-1.0) calculated by models')
            s.save()
            self.readers.add(s)

            #setpoint arrays
            s = Reader(name='SCOCc',help_text='Package Unit: occupied cooling setpoints (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SCUNc',help_text='Package Unit: unoccupied cooling setpoints (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SHOCc',help_text='Package Unit: occupied heating setpoints (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SHUNc',help_text='Package Unit: unoccupied heating setpoints (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='SRFCc',help_text='Package Unit: fully charged refrigerant pressures (lbs) calculated by models')
            s.save()
            self.readers.add(s)

            #status
            s = Reader(name='STONc',help_text='Package Unit: unit status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC1c',help_text='Package Unit: compressor 1 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC2c',help_text='Package Unit: compressor 2 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STC3c',help_text='Package Unit: compressor 3 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STH1c',help_text='Package Unit: heating 1 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STH2c',help_text='Package Unit: heating 2 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STECc',help_text='Package Unit: economizer status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STODc',help_text='Package Unit: outdoor air damper status (% open) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STSFc',help_text='Package Unit: supply fan status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STRFc',help_text='Package Unit: return fan status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STF1c',help_text='Package Unit: condenser fan 1 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STF2c',help_text='Package Unit: condenser fan 2 status (1/0) calculated by models')
            s.save()
            self.readers.add(s)
            
        super(PackageUnit, self).save(*args, **kwargs)
    def load_monther_period_dataframe(self, df):
        """Inputs:
            df
              (columns:Start Date, End Date,
                 Billing Demand,Peak Demand,
                 Consumption, kBtuh Peak Demand,
                 kBtu Consumption, Cost --
                 versions act, exp, base,
                 asave, esave; +deltas)
        
        Loads given dataframe into Monther
        by adding Monthlings, checking to
        ensure addition of new Monthlings
        will maintain date contiguity.
        
        Aimed at loading new date ranges.
        Calling functions should handle
        overwriting of existing data, cf.
        Meter model's upload_bill_data
        function."""
        try:
            if (('Start Date' in df.columns) and ('End Date' in df.columns) and
                ('Billing Demand (asave)' in df.columns) and ('Cost (asave)' in df.columns) and
                ('Peak Demand (asave)' in df.columns) and ('Consumption (asave)' in df.columns) and
                ('kBtuh Peak Demand (asave)' in df.columns) and ('kBtu Consumption (asave)' in df.columns) and
                ('Billing Demand (esave)' in df.columns) and ('Cost (esave)' in df.columns) and
                ('Peak Demand (esave)' in df.columns) and ('Consumption (esave)' in df.columns) and
                ('kBtuh Peak Demand (esave)' in df.columns) and ('kBtu Consumption (esave)' in df.columns) and
                ('Billing Demand (base)' in df.columns) and ('Cost (base)' in df.columns) and
                ('Peak Demand (base)' in df.columns) and ('Consumption (base)' in df.columns) and
                ('kBtuh Peak Demand (base)' in df.columns) and ('kBtu Consumption (base)' in df.columns) and
                ('Billing Demand (exp)' in df.columns) and ('Cost (exp)' in df.columns) and
                ('Peak Demand (exp)' in df.columns) and ('Consumption (exp)' in df.columns) and
                ('kBtuh Peak Demand (exp)' in df.columns) and ('kBtu Consumption (exp)' in df.columns) and
                ('Billing Demand (esave delta)' in df.columns) and ('Cost (esave delta)' in df.columns) and
                ('Peak Demand (esave delta)' in df.columns) and ('Consumption (esave delta)' in df.columns) and
                ('kBtuh Peak Demand (esave delta)' in df.columns) and ('kBtu Consumption (esave delta)' in df.columns) and
                ('Billing Demand (base delta)' in df.columns) and ('Cost (base delta)' in df.columns) and
                ('Peak Demand (base delta)' in df.columns) and ('Consumption (base delta)' in df.columns) and
                ('kBtuh Peak Demand (base delta)' in df.columns) and ('kBtu Consumption (base delta)' in df.columns) and
                ('Billing Demand (exp delta)' in df.columns) and ('Cost (exp delta)' in df.columns) and
                ('Peak Demand (exp delta)' in df.columns) and ('Consumption (exp delta)' in df.columns) and
                ('kBtuh Peak Demand (exp delta)' in df.columns) and ('kBtu Consumption (exp delta)' in df.columns) and
                ('Billing Demand (act)' in df.columns) and ('Cost (act)' in df.columns) and
                ('Peak Demand (act)' in df.columns) and ('Consumption (act)' in df.columns) and
                ('kBtuh Peak Demand (act)' in df.columns) and ('kBtu Consumption (act)' in df.columns) and
                ('CDD (peak demand)' in df.columns) and ('HDD (peak demand)' in df.columns) and
                ('CDD (consumption)' in df.columns) and ('HDD (consumption)' in df.columns) and
                ('X_a_cons' in df.columns) and ('X_b_cons' in df.columns) and
                ('X_a_pd' in df.columns) and ('X_b_pd' in df.columns) and
                (len(df)>0) and (type(df.index)==pd.tseries.period.PeriodIndex)  ):
                df = df.sort_index()
        except:
            m = Message(when=timezone.now(),
                        message_type='Code Error',
                        subject='Function received bad arguments.',
                        comment='Monther %s, load_monther_period_dataframe given improper dataframe input, function aborted.' % self.id)
            m.save()
            self.messages.add(m)
            print m
            success = False
        else:
            try:
                storedbd = self.get_monther_period_dataframe()
                if storedbd is None:
                    checkbd = df
                else:
                    checkbd = pd.concat([df,storedbd])
                    checkbd = checkbd.sort_index()
                    
                #check if combined data would be contiguous before loading into Monthlings
                #checking if less than 2 day delta since the 11,11,11 dates are off the non 11,11,11 dates
                #eventually should go back and ensure consistency, i.e. always use or don't use the 11,11,11 convention
                #contiguous_check = (checkbd['End Date'].shift(1) + timedelta(days=1)) == checkbd['Start Date']
                max_day_delta_bool = max(checkbd['Start Date'][1:].values - checkbd['End Date'][0:-1].values) < timedelta(days = 2)
            except:
                m = Message(when=timezone.now(),
                            message_type='Code Error',
                            subject='Model update failed.',
                            comment='Monther %s, load_monther_period_dataframe unable to check contiguity, function aborted.' % self.id)
                m.save()
                self.messages.add(m)
                print m
                success = False
            else:
                #if False in contiguous_check[1:].values: #1st is always False due to shift, but if other False then abort
                if not(max_day_delta_bool):
                    m = Message(when=timezone.now(),
                                message_type='Code Error',
                                subject='Function received bad arguments.',
                                comment='Monther %s, load_monther_period_dataframe given data not contiguous with existing data, function aborted.' % self.id)
                    m.save()
                    self.messages.add(m)
                    print m
                    success = False
                else:
                    try:
                        for i in range(0,len(df)):
                            per_date = UTC.localize(df.index[i].to_timestamp() + timedelta(days=10,hours=11,minutes=11,seconds=11)) #add days/hours/mins/secs to avoid crossing month boundary when adjusting timezones
                            if df['Start Date'][i].tzinfo is None:
                                start_i = UTC.localize(df['Start Date'][i])
                            else:
                                start_i = df['Start Date'][i]
                            if df['End Date'][i].tzinfo is None:
                                end_i = UTC.localize(df['End Date'][i])
                            else:
                                end_i = df['End Date'][i]
                            mlg = Monthling(
                                    when = per_date,
                                    start_date = start_i,
                                    end_date = end_i,
                                    
                                    cdd_peak_demand = Decimal(df['CDD (peak demand)'][i]),
                                    hdd_peak_demand = Decimal(df['HDD (peak demand)'][i]),
                                    cdd_consumption = Decimal(df['CDD (consumption)'][i]),
                                    hdd_consumption = Decimal(df['HDD (consumption)'][i]),
        
                                    base_billing_demand = Decimal(df['Billing Demand (base)'][i]),
                                    base_peak_demand = Decimal(df['Peak Demand (base)'][i]),
                                    base_consumption = Decimal(df['Consumption (base)'][i]),
                                    base_kBtuh_peak_demand = Decimal(df['kBtuh Peak Demand (base)'][i]),
                                    base_kBtu_consumption = Decimal(df['kBtu Consumption (base)'][i]),
                                    base_cost = Decimal(df['Cost (base)'][i]),
                                    exp_billing_demand = Decimal(df['Billing Demand (exp)'][i]),
                                    exp_peak_demand = Decimal(df['Peak Demand (exp)'][i]),
                                    exp_consumption = Decimal(df['Consumption (exp)'][i]),
                                    exp_kBtuh_peak_demand = Decimal(df['kBtuh Peak Demand (exp)'][i]),
                                    exp_kBtu_consumption = Decimal(df['kBtu Consumption (exp)'][i]),
                                    exp_cost = Decimal(df['Cost (exp)'][i]),
                                    act_billing_demand = Decimal(df['Billing Demand (act)'][i]),
                                    act_peak_demand = Decimal(df['Peak Demand (act)'][i]),
                                    act_consumption = Decimal(df['Consumption (act)'][i]),
                                    act_kBtuh_peak_demand = Decimal(df['kBtuh Peak Demand (act)'][i]),
                                    act_kBtu_consumption = Decimal(df['kBtu Consumption (act)'][i]),
                                    act_cost = Decimal(df['Cost (act)'][i]),
                                    esave_billing_demand = Decimal(df['Billing Demand (esave)'][i]),
                                    esave_peak_demand = Decimal(df['Peak Demand (esave)'][i]),
                                    esave_consumption = Decimal(df['Consumption (esave)'][i]),
                                    esave_kBtuh_peak_demand = Decimal(df['kBtuh Peak Demand (esave)'][i]),
                                    esave_kBtu_consumption = Decimal(df['kBtu Consumption (esave)'][i]),
                                    esave_cost = Decimal(df['Cost (esave)'][i]),
                                    asave_billing_demand = Decimal(df['Billing Demand (asave)'][i]),
                                    asave_peak_demand = Decimal(df['Peak Demand (asave)'][i]),
                                    asave_consumption = Decimal(df['Consumption (asave)'][i]),
                                    asave_kBtuh_peak_demand = Decimal(df['kBtuh Peak Demand (asave)'][i]),
                                    asave_kBtu_consumption = Decimal(df['kBtu Consumption (asave)'][i]),
                                    asave_cost = Decimal(df['Cost (asave)'][i]),
        
                                    base_billing_demand_delta = Decimal(df['Billing Demand (base delta)'][i]),
                                    base_peak_demand_delta = Decimal(df['Peak Demand (base delta)'][i]),
                                    base_consumption_delta = Decimal(df['Consumption (base delta)'][i]),
                                    base_kBtuh_peak_demand_delta = Decimal(df['kBtuh Peak Demand (base delta)'][i]),
                                    base_kBtu_consumption_delta = Decimal(df['kBtu Consumption (base delta)'][i]),
                                    base_cost_delta = Decimal(df['Cost (base delta)'][i]),
                                    exp_billing_demand_delta = Decimal(df['Billing Demand (exp delta)'][i]),
                                    exp_peak_demand_delta = Decimal(df['Peak Demand (exp delta)'][i]),
                                    exp_consumption_delta = Decimal(df['Consumption (exp delta)'][i]),
                                    exp_kBtuh_peak_demand_delta = Decimal(df['kBtuh Peak Demand (exp delta)'][i]),
                                    exp_kBtu_consumption_delta = Decimal(df['kBtu Consumption (exp delta)'][i]),
                                    exp_cost_delta = Decimal(df['Cost (exp delta)'][i]),
                                    esave_billing_demand_delta = Decimal(df['Billing Demand (esave delta)'][i]),
                                    esave_peak_demand_delta = Decimal(df['Peak Demand (esave delta)'][i]),
                                    esave_consumption_delta = Decimal(df['Consumption (esave delta)'][i]),
                                    esave_kBtuh_peak_demand_delta = Decimal(df['kBtuh Peak Demand (esave delta)'][i]),
                                    esave_kBtu_consumption_delta = Decimal(df['kBtu Consumption (esave delta)'][i]),
                                    esave_cost_delta = Decimal(df['Cost (esave delta)'][i]),
        
                                    X_a_cons = Decimal(df['X_a_cons'][i]),
                                    X_b_cons = Decimal(df['X_b_cons'][i]),
                                    X_a_pd = Decimal(df['X_a_pd'][i]),
                                    X_b_pd = Decimal(df['X_b_pd'][i]),

                                    monther = self)
                            mlg.save()
                    except:
                        m = Message(when=timezone.now(),
                                    message_type='Code Error',
                                    subject='Model update failed.',
                                    comment='Monther %s load_monther_period_dataframe unable to create and save all Monthlings.' % self.id)
                        m.save()
                        self.messages.add(m)
                        print m
                        success = False
                    else:
                        m = Message(when=timezone.now(),
                                    message_type='Code Success',
                                    subject='Model updated.',
                                    comment='Loaded dataframe into Monther %s.' % self.id)
                        m.save()
                        self.messages.add(m)
                        print m
                        success = True
        return success
    def save(self, *args, **kwargs):
        if self.id is None:
            super(Equipment, self).save(*args, **kwargs)
            tnow = timezone.now()
            m = Message(when = tnow,
                        message_type = 'Model Info',
                        subject = 'Model created.',
                        comment = 'This Equipment was created on %s.' % tnow)
            m.save()
            self.messages.add(m)
            
            #universal energy units kBtu (for comparing multiple fuels on common unit basis)
            s = Reader(name='kBtuo',help_text='total: energy (kBtu) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='kBtup',help_text='total: energy (kBtu) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='kBtuc',help_text='total: energy (kBtu) calculated by models')
            s.save()
            self.readers.add(s)
            
            #---------------------------------------------------------------------observed
            #electricity
            s = Reader(name='ELCAo',help_text='electricity: current (A) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELVVo',help_text='electricity: voltage (V) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELPHo',help_text='electricity: phase (1,3) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELPFo',help_text='electricity: power factor (dimensionless) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELKWo',help_text='electricity: power (kW) observed by local sensors')
            s.save()
            self.readers.add(s)
            
            #natural gas
            s = Reader(name='NGINo',help_text='natural gas: input power (Btu/h) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGOUo',help_text='natural gas: delivered power (Btu/h) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGEFo',help_text='natural gas: thermal efficiency (dimensionless) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGFLo',help_text='natural gas: flow rate (ft^3/min) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGHVo',help_text='natural gas: heating value (Btu/ft^3) observed by local sensors')
            s.save()
            self.readers.add(s)
            
            #steam
            s = Reader(name='STHIo',help_text='steam: input enthalpy (Btu/lb) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STHOo',help_text='steam: output enthalpy (Btu/lb) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STBHo',help_text='steam: delivered power (Btu/h) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='STFLo',help_text='steam: flow rate (lb/hr) observed by local sensors')
            s.save()
            self.readers.add(s)
            
            #chilled water
            s = Reader(name='CWFLo',help_text='chilled water: flow rate (gal/min) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTIo',help_text='chilled water: inlet temperature (' + u"\u00b0" + 'F) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTOo',help_text='chilled water: outlet temperature (' + u"\u00b0" + 'F) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTNo',help_text='chilled water: delivered power (tons) observed by local sensors')
            s.save()
            self.readers.add(s)
            
            #hot water
            s = Reader(name='HWFLo',help_text='hot water: flow rate (gal/min) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWTIo',help_text='hot water: inlet temperature (' + u"\u00b0" + 'F) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWTOo',help_text='hot water: outlet temperature (' + u"\u00b0" + 'F) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWBHo',help_text='hot water: delivered power (Btu/h) observed by local sensors')
            s.save()
            self.readers.add(s)
            
            #fuel oil
            s = Reader(name='FOBIo',help_text='fuel oil: input power (Btu/h) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOBOo',help_text='fuel oil: delivered power (Btu/h) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOEFo',help_text='fuel oil: thermal efficiency (dimensionless) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOFLo',help_text='fuel oil: flow rate (gal/min) observed by local sensors')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOHVo',help_text='fuel oil: heating value (Btu/gal) observed by local sensors')
            s.save()
            self.readers.add(s)

            #---------------------------------------------------------------------provided
            #electricity
            s = Reader(name='ELCAp',help_text='electricity: current (A) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELVVp',help_text='electricity: voltage (V) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELPHp',help_text='electricity: phase (1,3) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELPFp',help_text='electricity: power factor (dimensionless) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELKWp',help_text='electricity: power (kW) provided by remote source')
            s.save()
            self.readers.add(s)
            
            #natural gas
            s = Reader(name='NGINp',help_text='natural gas: input power (Btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGOUp',help_text='natural gas: delivered power (Btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGEFp',help_text='natural gas: thermal efficiency (dimensionless) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGFLp',help_text='natural gas: flow rate (ft^3/min) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGHVp',help_text='natural gas: heating value (Btu/ft^3) provided by remote source')
            s.save()
            self.readers.add(s)
            
            #steam
            s = Reader(name='STHIp',help_text='steam: input enthalpy (Btu/lb) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STHOp',help_text='steam: output enthalpy (Btu/lb) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STBHp',help_text='steam: delivered power (Btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='STFLp',help_text='steam: flow rate (lb/hr) provided by remote source')
            s.save()
            self.readers.add(s)
            
            #chilled water
            s = Reader(name='CWFLp',help_text='chilled water: flow rate (gal/min) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTIp',help_text='chilled water: inlet temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTOp',help_text='chilled water: outlet temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTNp',help_text='chilled water: delivered power (tons) provided by remote source')
            s.save()
            self.readers.add(s)
            
            #hot water
            s = Reader(name='HWFLp',help_text='hot water: flow rate (gal/min) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWTIp',help_text='hot water: inlet temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWTOp',help_text='hot water: outlet temperature (' + u"\u00b0" + 'F) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWBHp',help_text='hot water: delivered power (Btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            
            #fuel oil
            s = Reader(name='FOBIp',help_text='fuel oil: input power (Btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOBOp',help_text='fuel oil: delivered power (Btu/h) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOEFp',help_text='fuel oil: thermal efficiency (dimensionless) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOFLp',help_text='fuel oil: flow rate (gal/min) provided by remote source')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOHVp',help_text='fuel oil: heating value (Btu/gal) provided by remote source')
            s.save()
            self.readers.add(s)

            #---------------------------------------------------------------------calculated
            #electricity
            s = Reader(name='ELCAc',help_text='electricity: current (A) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELVVc',help_text='electricity: voltage (V) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELPHc',help_text='electricity: phase (1,3) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELPFc',help_text='electricity: power factor (dimensionless) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='ELKWc',help_text='electricity: power (kW) calculated by models')
            s.save()
            self.readers.add(s)
            
            #natural gas
            s = Reader(name='NGINc',help_text='natural gas: input power (Btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGOUc',help_text='natural gas: delivered power (Btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGEFc',help_text='natural gas: thermal efficiency (dimensionless) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGFLc',help_text='natural gas: flow rate (ft^3/min) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='NGHVc',help_text='natural gas: heating value (Btu/ft^3) calculated by models')
            s.save()
            self.readers.add(s)
            
            #steam
            s = Reader(name='STHIc',help_text='steam: input enthalpy (Btu/lb) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STHOc',help_text='steam: output enthalpy (Btu/lb) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STBHc',help_text='steam: delivered power (Btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='STFLc',help_text='steam: flow rate (lb/hr) calculated by models')
            s.save()
            self.readers.add(s)
            
            #chilled water
            s = Reader(name='CWFLc',help_text='chilled water: flow rate (gal/min) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTIc',help_text='chilled water: inlet temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTOc',help_text='chilled water: outlet temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='CWTNc',help_text='chilled water: delivered power (tons) calculated by models')
            s.save()
            self.readers.add(s)
            
            #hot water
            s = Reader(name='HWFLc',help_text='hot water: flow rate (gal/min) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWTIc',help_text='hot water: inlet temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWTOc',help_text='hot water: outlet temperature (' + u"\u00b0" + 'F) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='HWBHc',help_text='hot water: delivered power (Btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            
            #fuel oil
            s = Reader(name='FOBIc',help_text='fuel oil: input power (Btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOBOc',help_text='fuel oil: delivered power (Btu/h) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOEFc',help_text='fuel oil: thermal efficiency (dimensionless) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOFLc',help_text='fuel oil: flow rate (gal/min) calculated by models')
            s.save()
            self.readers.add(s)
            s = Reader(name='FOHVc',help_text='fuel oil: heating value (Btu/gal) calculated by models')
            s.save()
            self.readers.add(s)


        super(Equipment, self).save(*args, **kwargs)