예제 #1
0
    def init_alg(self, exp_uid, algorithm, alg_args):
        butler = Butler(self.app_id, exp_uid, self.myApp.TargetManager,
                        self.butler.db, self.butler.ell,
                        algorithm['alg_label'], algorithm['alg_id'])
        alg = utils.get_app_alg(self.app_id, algorithm['alg_id'])

        if 'args' in self.algs_reference_dict['initExp']:
            alg_args = verifier.verify(
                alg_args, self.algs_reference_dict['initExp']['args'])

        # I got rid of a timeit function here; it wasn't handling the
        # argument unpacking correctly? --Scott, 2016-3-7
        # TODO: put dt back in and change log_entry to relfect that
        alg_response = alg.initExp(butler, **alg_args)
        alg_response = verifier.verify(
            {'rets': alg_response},
            {'rets': self.algs_reference_dict['initExp']['rets']})
        log_entry = {
            'exp_uid': exp_uid,
            'alg_label': algorithm['alg_label'],
            'task': 'initExp',
            'duration': -1,
            'timestamp': utils.datetimeNow()
        }
        self.butler.log('ALG-DURATION', log_entry)
예제 #2
0
 def run_alg(self, butler, alg_label, alg, func_name, alg_args):
     if 'args' in self.algs_reference_dict[func_name]:
         alg_args = verifier.verify(alg_args, self.algs_reference_dict[func_name]['args'])
     alg_response, dt = utils.timeit(getattr(alg, func_name))(butler, **alg_args)
     alg_response = verifier.verify({'rets':alg_response},
                                    {'rets':self.algs_reference_dict[func_name]['rets']})
     log_entry_durations = {'exp_uid':self.exp_uid,
                            'alg_label':alg_label,
                            'task':func_name,
                            'duration':dt}
     log_entry_durations.update(butler.algorithms.getDurations())
     self.log_entry_durations = log_entry_durations
     return alg_response['rets']
예제 #3
0
파일: App.py 프로젝트: nextml/NEXT
 def run_alg(self, butler, alg_label, alg, func_name, alg_args):
     if 'args' in self.algs_reference_dict[func_name]:
         alg_args = verifier.verify(alg_args, self.algs_reference_dict[func_name]['args'])
     alg_response, dt = utils.timeit(getattr(alg, func_name))(butler, **alg_args)
     alg_response = verifier.verify({'rets':alg_response},
                                    {'rets':self.algs_reference_dict[func_name]['rets']})
     log_entry_durations = {'exp_uid':self.exp_uid,
                            'alg_label':alg_label,
                            'task':func_name,
                            'duration':dt}
     log_entry_durations.update(butler.algorithms.getDurations())
     self.log_entry_durations = log_entry_durations
     return alg_response['rets']
예제 #4
0
파일: App.py 프로젝트: nextml/NEXT
    def init_alg(self, exp_uid, algorithm, alg_args):
        butler = Butler(self.app_id, exp_uid, self.myApp.TargetManager, self.butler.db, self.butler.ell, algorithm['alg_label'], algorithm['alg_id'])
        alg = utils.get_app_alg(self.app_id, algorithm['alg_id'])

        if 'args' in self.algs_reference_dict['initExp']:
            alg_args = verifier.verify(alg_args, self.algs_reference_dict['initExp']['args'])

        # I got rid of a timeit function here; it wasn't handling the
        # argument unpacking correctly? --Scott, 2016-3-7
        # TODO: put dt back in and change log_entry to relfect that
        alg_response = alg.initExp(butler, **alg_args)
        alg_response = verifier.verify({'rets':alg_response}, {'rets':self.algs_reference_dict['initExp']['rets']})
        log_entry = {'exp_uid':exp_uid, 'alg_label':algorithm['alg_label'], 'task':'initExp', 'duration':-1, 'timestamp':utils.datetimeNow()}
        self.butler.log('ALG-DURATION', log_entry)
예제 #5
0
    def getModel(self, exp_uid, args_json):
        try:
            args_dict = self.helper.convert_json(args_json)
            args_dict = verifier.verify(args_dict, self.reference_dict['getModel']['args']) 
            alg_label = args_dict['args']['alg_label']
            args = self.butler.experiment.get(key='args')
            for algorithm in args['alg_list']:
                if alg_label == algorithm['alg_label']:
                    alg_id = algorithm['alg_id']

            myapp_response = self.call_app_fn(alg_label, alg_id, 'getModel', args_dict)

            myapp_response['exp_uid'] = exp_uid
            myapp_response['alg_label'] = alg_label
            # Log the response of the getModel in ALG-EVALUATION
            if args_dict['args']['logging']:
                alg_log_entry = {'exp_uid': exp_uid, 'alg_label':alg_label, 'task': 'getModel', 'timestamp': str(utils.datetimeNow())}
                alg_log_entry.update(myapp_response)
                self.butler.log('ALG-EVALUATION', alg_log_entry)
            return json.dumps({'args': myapp_response,
                               'meta': {'log_entry_durations':self.log_entry_durations,
                                        'timestamp': str(utils.datetimeNow())}}), True, ''
        except Exception, error:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            full_error = str(traceback.format_exc())+'\n'+str(error)
            utils.debug_print("getModel Exception: " + full_error, color='red')
            log_entry = { 'exp_uid':exp_uid,'task':'getModel','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json } 
            self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
            traceback.print_tb(exc_traceback)       
            return Exception(error)
예제 #6
0
    def processAnswer(self, exp_uid, args_json):
        try:
            args_dict = self.helper.convert_json(args_json)
            args_dict = verifier.verify(args_dict, self.reference_dict['processAnswer']['args'])
            # Update timing info in query
            query = self.butler.queries.get(uid=args_dict['args']['query_uid'])
            timestamp_answer_received = args_dict['args'].get('timestamp_answer_received', None)
            delta_datetime = utils.str2datetime(timestamp_answer_received) - \
                             utils.str2datetime(query['timestamp_query_generated'])
            round_trip_time = delta_datetime.total_seconds()
            response_time = float(args_dict['args'].get('response_time',0.))

            query_update = self.call_app_fn(query['alg_label'], query['alg_id'], 'processAnswer', args_dict)
            query_update.update({'response_time':response_time,
                                 'network_delay':round_trip_time - response_time,
                                 'timestamp_answer_received': timestamp_answer_received
                                 })
            self.butler.queries.set_many(uid=args_dict['args']['query_uid'],key_value_dict=query_update)

            return json.dumps({'args': {}, 'meta': {'log_entry_durations':self.log_entry_durations}}), True, ''
        
        except Exception, error:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            full_error = str(traceback.format_exc())+'\n'+str(error)
            utils.debug_print("processAnswer Exception: " + full_error, color='red')
            log_entry = { 'exp_uid':exp_uid,'task':'processAnswer','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json } 
            self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
    	    traceback.print_tb(exc_traceback)
    	    raise Exception(error)
예제 #7
0
파일: App.py 프로젝트: nextml/NEXT
    def getModel(self, exp_uid, args_json):
        try:
            args_dict = self.helper.convert_json(args_json)
            args_dict = verifier.verify(args_dict, self.reference_dict['getModel']['args'])
            alg_label = args_dict['args']['alg_label']
            args = self.butler.experiment.get(key='args')
            for algorithm in args['alg_list']:
                if alg_label == algorithm['alg_label']:
                    alg_id = algorithm['alg_id']

            myapp_response = self.call_app_fn(alg_label, alg_id, 'getModel', args_dict)

            myapp_response['exp_uid'] = exp_uid
            myapp_response['alg_label'] = alg_label
            # Log the response of the getModel in ALG-EVALUATION
            if args_dict['args']['logging']:
                alg_log_entry = {'exp_uid': exp_uid, 'alg_label':alg_label, 'task': 'getModel', 'timestamp': str(utils.datetimeNow())}
                alg_log_entry.update(myapp_response)
                self.butler.log('ALG-EVALUATION', alg_log_entry)
            return json.dumps({'args': myapp_response,
                               'meta': {'log_entry_durations':self.log_entry_durations,
                                        'timestamp': str(utils.datetimeNow())}}), True, ''
        except Exception, error:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            full_error = str(traceback.format_exc())+'\n'+str(error)
            utils.debug_print("getModel Exception: " + full_error, color='red')
            log_entry = { 'exp_uid':exp_uid,'task':'getModel','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json }
            self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
            traceback.print_tb(exc_traceback)
            return Exception(error)
예제 #8
0
파일: App.py 프로젝트: nextml/NEXT
    def processAnswer(self, exp_uid, args_json):
        try:
            args_dict = self.helper.convert_json(args_json)
            args_dict = verifier.verify(args_dict, self.reference_dict['processAnswer']['args'])
            # Update timing info in query
            query = self.butler.queries.get(uid=args_dict['args']['query_uid'])
            timestamp_answer_received = args_dict['args'].get('timestamp_answer_received', None)
            delta_datetime = utils.str2datetime(timestamp_answer_received) - \
                             utils.str2datetime(query['timestamp_query_generated'])
            round_trip_time = delta_datetime.total_seconds()
            response_time = float(args_dict['args'].get('response_time',0.))

            query_update = self.call_app_fn(query['alg_label'], query['alg_id'], 'processAnswer', args_dict)
            query_update.update({'response_time':response_time,
                                 'network_delay':round_trip_time - response_time,
                                 'timestamp_answer_received': timestamp_answer_received
                                 })
            self.butler.queries.set_many(uid=args_dict['args']['query_uid'],key_value_dict=query_update)

            return json.dumps({'args': {}, 'meta': {'log_entry_durations':self.log_entry_durations}}), True, ''

        except Exception, error:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            full_error = str(traceback.format_exc())+'\n'+str(error)
            utils.debug_print("processAnswer Exception: " + full_error, color='red')
            log_entry = { 'exp_uid':exp_uid,'task':'processAnswer','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json }
            self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
    	    traceback.print_tb(exc_traceback)
    	    raise Exception(error)
예제 #9
0
파일: tasks.py 프로젝트: dconathan/NEXT
def apply_dashboard(app_id, exp_uid, args_in_json, enqueue_timestamp):
	enqueue_datetime = next.utils.str2datetime(enqueue_timestamp)
	dequeue_datetime = next.utils.datetimeNow()
	delta_datetime = dequeue_datetime - enqueue_datetime
	time_enqueued = delta_datetime.seconds + delta_datetime.microseconds/1000000.
        dir, _ = os.path.split(__file__)
        reference_dict,errs = verifier.load_doc('{}/myApp.yaml'.format(app_id, app_id),"apps/")
        if len(errs) > 0:
                raise Exception("App YAML format errors: \n{}".format(str(errs)))
        args_dict = verifier.verify(args_in_json, reference_dict['getStats']['args'])
        stat_id = args_dict['args'].get('stat_id','none')

        stat_args = args_dict['args']

        hash_object = hashlib.md5(stat_id+'_'+json.dumps(stat_args['params']))
        stat_uid = hash_object.hexdigest()
        stat_uid += '_' + exp_uid

        app = App_Wrapper(app_id, exp_uid, db, ell)
        cached_doc = app.butler.dashboard.get(uid=stat_uid)
        cached_response = None
        if (int(stat_args.get('force_recompute',0))==0) and (cached_doc is not None):    
          delta_datetime = (next.utils.datetimeNow() - next.utils.str2datetime(cached_doc['timestamp']))
          if delta_datetime.seconds < next.constants.DASHBOARD_STALENESS_IN_SECONDS:
            cached_response = json.loads(cached_doc['data_dict'])
            if 'meta' not in cached_response:
              cached_response['meta']={}
            cached_response['meta']['cached'] = 1
            if delta_datetime.seconds/60<1:
                cached_response['meta']['last_dashboard_update'] = '<1 minute ago'
            else:
                cached_response['meta']['last_dashboard_update'] = str(delta_datetime.seconds/60)+' minutes ago'

        if cached_response==None:
            dashboard_string = 'apps.' + app_id + '.dashboard.Dashboard'
            dashboard_module = __import__(dashboard_string, fromlist=[''])
            dashboard = getattr(dashboard_module, 'MyAppDashboard')
            dashboard = dashboard(db, ell)
            stats_method = getattr(dashboard, stat_id)
            response,dt = next.utils.timeit(stats_method)(app,app.butler,**args_dict['args']['params'])
            
            save_dict = {'exp_uid':app.exp_uid,
                    'stat_uid':stat_uid,
                    'timestamp':next.utils.datetime2str(next.utils.datetimeNow()),
                    'data_dict':json.dumps(response)}
            app.butler.dashboard.set_many(uid=stat_uid,key_value_dict=save_dict)

            # update the admin timing with the timing of a getModel
            if hasattr(app, 'log_entry_durations'):
                app.log_entry_durations['app_duration'] = dt
                app.log_entry_durations['duration_enqueued'] = time_enqueued
                app.butler.ell.log(app.app_id+':ALG-DURATION', app.log_entry_durations)
        else:
            response = cached_response

        if DEBUG_ON:
            next.utils.debug_print('#### Finished Dashboard %s, time_enqueued=%s,  execution_time=%s ####' % (stat_id, time_enqueued, dt), color='white')
	return json.dumps(response), True, ''
예제 #10
0
    def getQuery(self, exp_uid, args_json):
        try:
    	    args_dict = self.helper.convert_json(args_json)
            args_dict = verifier.verify(args_dict, self.reference_dict['getQuery']['args'])
            experiment_dict = self.butler.experiment.get()
            alg_list = experiment_dict['args']['alg_list']
            participant_to_algorithm_management = experiment_dict['args']['participant_to_algorithm_management']
            algorithm_management_settings = experiment_dict['args']['algorithm_management_settings']
            # Create the participant dictionary in participants bucket if needed. Also pull out label and id for this algorithm
            participant_uid = args_dict['args'].get('participant_uid', args_dict['exp_uid'])
            # Check to see if the first participant has come by and if not, save to db
            participant_doc = self.butler.participants.get(uid=participant_uid)
            first_participant_query = participant_doc==None
            if first_participant_query:
                participant_doc = {}
                self.butler.participants.set(uid=participant_uid, value={'exp_uid':exp_uid, 'participant_uid':participant_uid})
            if (participant_uid == exp_uid) or (participant_to_algorithm_management == 'one_to_many') or (first_participant_query):

                if algorithm_management_settings['mode'] == 'fixed_proportions':
                    prop = [prop_item['proportion'] for prop_item in algorithm_management_settings['params']]
                    chosen_alg = numpy.random.choice(alg_list, p=prop)
                elif algorithm_management_settings['mode'] == 'custom' :
                    chosen_alg = self.myApp.chooseAlg(self.butler, alg_list, args_dict['args'])
                else:
                    chosen_alg = numpy.random.choice(alg_list)

                alg_id = chosen_alg['alg_id']
                alg_label = chosen_alg['alg_label']
                if (first_participant_query) and (participant_to_algorithm_management=='one_to_one'):
                    self.butler.participants.set(uid=participant_uid, key='alg_id',value=alg_id)
                    self.butler.participants.set(uid=participant_uid, key='alg_label',value=alg_label)
            elif (participant_to_algorithm_management=='one_to_one'):
                alg_id = participant_doc['alg_id']
                alg_label = participant_doc['alg_label']

            query_uid = utils.getNewUID()
            args_dict['args'].update(query_uid=query_uid)
            query_doc = self.call_app_fn(alg_label, alg_id, 'getQuery', args_dict)
            
            query_doc.update({'participant_uid':participant_uid,
                              'alg_id':alg_id,
                              'exp_uid':exp_uid,
                              'alg_label':alg_label,
                              'timestamp_query_generated':str(utils.datetimeNow()),
                              'query_uid':query_uid})
            self.butler.queries.set(uid=query_uid, value=query_doc)
            return json.dumps({'args':query_doc,'meta':{'log_entry_durations':self.log_entry_durations}}), True,''
        except Exception, error:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            full_error = str(traceback.format_exc())+'\n'+str(error)
            utils.debug_print("getQuery Exception: " + full_error, color='red')
            log_entry = { 'exp_uid':exp_uid,'task':'getQuery','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json } 
            self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
            traceback.print_tb(exc_traceback)
            return '{}', False, str(error)
예제 #11
0
 def initExp(self, exp_uid, args_json):
     try:
         self.helper.ensure_indices(self.app_id,self.butler.db, self.butler.ell)
         args_dict = self.helper.convert_json(args_json)
         args_dict = verifier.verify(args_dict, self.reference_dict['initExp']['args'])
         args_dict['exp_uid'] = exp_uid # to get doc from db
         args_dict['start_date'] = utils.datetime2str(utils.datetimeNow())
         self.butler.admin.set(uid=exp_uid,value={'exp_uid': exp_uid, 'app_id':self.app_id, 'start_date':str(utils.datetimeNow())})            
         utils.debug_print("ASD "+str(args_dict))
         args_dict['args'] = self.init_app(exp_uid, args_dict['args']['alg_list'], args_dict['args'])
         args_dict['git_hash'] = git_hash
         self.butler.experiment.set(value=args_dict)
         return '{}', True, ''
     except Exception, error:
         exc_type, exc_value, exc_traceback = sys.exc_info()
         full_error = str(traceback.format_exc())+'\n'+str(error)
         utils.debug_print("initExp Exception: " + full_error, color='red')
         log_entry = { 'exp_uid':exp_uid,'task':'initExp','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json } 
         self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
         traceback.print_tb(exc_traceback)
         return '{}', False, str(error)
예제 #12
0
파일: App.py 프로젝트: nextml/NEXT
 def initExp(self, exp_uid, args_json):
     try:
         self.helper.ensure_indices(self.app_id,self.butler.db, self.butler.ell)
         args_dict = self.helper.convert_json(args_json)
         args_dict = verifier.verify(args_dict, self.reference_dict['initExp']['args'])
         args_dict['exp_uid'] = exp_uid # to get doc from db
         args_dict['start_date'] = utils.datetime2str(utils.datetimeNow())
         self.butler.admin.set(uid=exp_uid,value={'exp_uid': exp_uid, 'app_id':self.app_id, 'start_date':str(utils.datetimeNow())})
         self.butler.experiment.set(value={'exp_uid': exp_uid})
         args_dict['args'] = self.init_app(exp_uid, args_dict['args']['alg_list'], args_dict['args'])
         args_dict['git_hash'] = git_hash
         self.butler.experiment.set_many(key_value_dict=args_dict)
         return '{}', True, ''
     except Exception, error:
         exc_type, exc_value, exc_traceback = sys.exc_info()
         full_error = str(traceback.format_exc())+'\n'+str(error)
         utils.debug_print("initExp Exception: " + full_error, color='red')
         log_entry = { 'exp_uid':exp_uid,'task':'initExp','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json }
         self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
         traceback.print_tb(exc_traceback)
         return '{}', False, str(error)
예제 #13
0
파일: App.py 프로젝트: nextml/NEXT
    def getQuery(self, exp_uid, args_json):
        try:
    	    args_dict = self.helper.convert_json(args_json)
            args_dict = verifier.verify(args_dict, self.reference_dict['getQuery']['args'])
            experiment_dict = self.butler.experiment.get()
            alg_list = experiment_dict['args']['alg_list']
            participant_to_algorithm_management = experiment_dict['args']['participant_to_algorithm_management']
            algorithm_management_settings = experiment_dict['args']['algorithm_management_settings']
            # Create the participant dictionary in participants bucket if needed. Also pull out label and id for this algorithm
            participant_uid = args_dict['args'].get('participant_uid', args_dict['exp_uid'])
            # Check to see if the first participant has come by and if not, save to db
            participant_doc = self.butler.participants.get(uid=participant_uid)
            first_participant_query = participant_doc==None
            if first_participant_query:
                participant_doc = {}
                self.butler.participants.set(uid=participant_uid, value={'exp_uid':exp_uid, 'participant_uid':participant_uid})
            if (participant_uid == exp_uid) or (participant_to_algorithm_management == 'one_to_many') or (first_participant_query):

                if algorithm_management_settings['mode'] == 'fixed_proportions':
                    labels = [alg['alg_label'] for alg in algorithm_management_settings['params']]
                    prop = [prop_item['proportion'] for prop_item in algorithm_management_settings['params']]
                    # reorder prop and alg_list to have same order
                    new_alg_list = []
                    broken = False
                    for label in labels:
                        broken = False
                        for alg in alg_list:
                            if label == alg['alg_label']:
                                new_alg_list += [alg]
                                broken = True
                                break
                        if not broken:
                            raise Exception('alg_label not present for both porportions and labels')
                    chosen_alg = numpy.random.choice(new_alg_list, p=prop)
                elif algorithm_management_settings['mode'] == 'custom' :
                    chosen_alg = self.myApp.chooseAlg(self.butler, alg_list, args_dict['args'])
                else:
                    chosen_alg = numpy.random.choice(alg_list)

                alg_id = chosen_alg['alg_id']
                alg_label = chosen_alg['alg_label']
                if (first_participant_query) and (participant_to_algorithm_management=='one_to_one'):
                    self.butler.participants.set(uid=participant_uid, key='alg_id',value=alg_id)
                    self.butler.participants.set(uid=participant_uid, key='alg_label',value=alg_label)
            elif (participant_to_algorithm_management=='one_to_one'):
                alg_id = participant_doc['alg_id']
                alg_label = participant_doc['alg_label']

            query_uid = utils.getNewUID()
            args_dict['args'].update(query_uid=query_uid)
            query_doc = self.call_app_fn(alg_label, alg_id, 'getQuery', args_dict)

            query_doc.update({'participant_uid':participant_uid,
                              'alg_id':alg_id,
                              'exp_uid':exp_uid,
                              'alg_label':alg_label,
                              'timestamp_query_generated':str(utils.datetimeNow()),
                              'query_uid':query_uid})
            self.butler.queries.set(uid=query_uid, value=query_doc)
            return json.dumps({'args':query_doc,'meta':{'log_entry_durations':self.log_entry_durations}}), True,''
        except Exception, error:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            full_error = str(traceback.format_exc())+'\n'+str(error)
            utils.debug_print("getQuery Exception: " + full_error, color='red')
            log_entry = { 'exp_uid':exp_uid,'task':'getQuery','error':full_error,'timestamp':utils.datetimeNow(),'args_json':args_json }
            self.butler.ell.log( self.app_id+':APP-EXCEPTION', log_entry  )
            traceback.print_tb(exc_traceback)
            return '{}', False, str(error)
예제 #14
0
파일: tasks.py 프로젝트: abiswas3/NEXT
def apply_dashboard(app_id, exp_uid, args_in_json, enqueue_timestamp):
    enqueue_datetime = next.utils.str2datetime(enqueue_timestamp)
    dequeue_datetime = next.utils.datetimeNow()
    delta_datetime = dequeue_datetime - enqueue_datetime
    time_enqueued = delta_datetime.seconds + delta_datetime.microseconds / 1000000.
    dir, _ = os.path.split(__file__)
    reference_dict, errs = verifier.load_doc(
        '{}/{}.yaml'.format(app_id, app_id), "apps/")
    if len(errs) > 0:
        raise Exception("App YAML format errors: \n{}".format(str(errs)))
    args_dict = verifier.verify(args_in_json,
                                reference_dict['getStats']['args'])
    stat_id = args_dict['args'].get('stat_id', 'none')

    stat_args = args_dict['args']
    hash_object = hashlib.md5(stat_id + '_' + json.dumps(stat_args['params']))
    stat_uid = hash_object.hexdigest()

    app = App_Wrapper(app_id, exp_uid, db, ell)
    cached_doc = app.butler.dashboard.get(uid=stat_uid)
    cached_response = None
    if (int(stat_args.get('force_recompute', 0)) == 0) and (cached_doc
                                                            is not None):
        delta_datetime = (next.utils.datetimeNow() -
                          next.utils.str2datetime(cached_doc['timestamp']))
        if delta_datetime.seconds < next.constants.DASHBOARD_STALENESS_IN_SECONDS:
            cached_response = json.loads(cached_doc['data_dict'])
            if 'meta' not in cached_response:
                cached_response['meta'] = {}
            cached_response['meta']['cached'] = 1
            if delta_datetime.seconds / 60 < 1:
                cached_response['meta'][
                    'last_dashboard_update'] = '<1 minute ago'
            else:
                cached_response['meta']['last_dashboard_update'] = str(
                    delta_datetime.seconds / 60) + ' minutes ago'

    if cached_response == None:
        dashboard_string = 'apps.' + app_id + '.dashboard.Dashboard'
        dashboard_module = __import__(dashboard_string, fromlist=[''])
        dashboard = getattr(dashboard_module, app_id + 'Dashboard')
        dashboard = dashboard(db, ell)
        stats_method = getattr(dashboard, stat_id)
        response, dt = next.utils.timeit(stats_method)(
            app, app.butler, **args_dict['args']['params'])

        save_dict = {
            'exp_uid': app.exp_uid,
            'stat_uid': stat_uid,
            'timestamp': next.utils.datetime2str(next.utils.datetimeNow()),
            'data_dict': json.dumps(response)
        }
        app.butler.dashboard.set_many(uid=stat_uid, key_value_dict=save_dict)

        # update the admin timing with the timing of a getModel
        if hasattr(app, 'log_entry_durations'):
            app.log_entry_durations['app_duration'] = dt
            app.log_entry_durations['duration_enqueued'] = time_enqueued
            app.butler.ell.log(app.app_id + ':ALG-DURATION',
                               app.log_entry_durations)
    else:
        response = cached_response

    if DEBUG_ON:
        next.utils.debug_print(
            '#### Finished Dashboard %s, time_enqueued=%s,  execution_time=%s ####'
            % (stat_id, time_enqueued, dt),
            color='white')
    return json.dumps(response), True, ''