def get_context_data(self, **kwargs): session_config_summaries = [ info_about_session_config(session_config) for session_config in SESSION_CONFIGS_DICT.values() ] kwargs.update({'session_config_summaries': session_config_summaries}) return super(CreateSession, self).get_context_data(**kwargs)
def get_context_data(self, **kwargs): title = getattr(settings, 'DEMO_PAGE_TITLE', 'Demo') intro_html = ( getattr(settings, 'DEMO_PAGE_INTRO_HTML', '') or getattr(settings, 'DEMO_PAGE_INTRO_TEXT', '')) context = super(DemoIndex, self).get_context_data(**kwargs) session_info = [] for session_config in SESSION_CONFIGS_DICT.values(): session_info.append( { 'name': session_config['name'], 'display_name': session_config['display_name'], 'url': reverse( 'CreateDemoSession', args=(session_config['name'],) ), 'num_demo_participants': session_config[ 'num_demo_participants' ] } ) context.update({ 'session_info': session_info, 'title': title, 'intro_html': intro_html, 'is_debug': settings.DEBUG, }) return context
def vars_for_template(self): title = getattr(settings, 'DEMO_PAGE_TITLE', 'Demo') intro_html = getattr(settings, 'DEMO_PAGE_INTRO_HTML', '') session_info = [] for session_config in SESSION_CONFIGS_DICT.values(): session_info.append({ 'name': session_config['name'], 'display_name': session_config['display_name'], 'url': self.request.url_for('CreateDemoSession', config_name=session_config['name']), 'num_demo_participants': session_config['num_demo_participants'], }) if os.environ.get('OTREEHUB_PUB'): otreehub_app_name = os.environ.get('OTREEHUB_APP_NAME') otreehub_url = f'https://www.otreehub.com/projects/{otreehub_app_name}/' else: otreehub_url = '' return dict( session_info=session_info, title=title, intro_html=intro_html, is_debug=settings.DEBUG, otreehub_url=otreehub_url, )
def get_context_data(self, **kwargs): title = getattr(settings, 'DEMO_PAGE_TITLE', 'Demo') intro_html = getattr(settings, 'DEMO_PAGE_INTRO_HTML', '') or getattr( settings, 'DEMO_PAGE_INTRO_TEXT', '') session_info = [] for session_config in SESSION_CONFIGS_DICT.values(): session_info.append({ 'name': session_config['name'], 'display_name': session_config['display_name'], 'url': reverse('CreateDemoSession', args=(session_config['name'], )), 'num_demo_participants': session_config['num_demo_participants'], }) if os.environ.get('OTREEHUB_PUB'): otreehub_app_name = os.environ.get('OTREEHUB_APP_NAME') otreehub_url = f'https://www.otreehub.com/projects/{otreehub_app_name}/' else: otreehub_url = '' return super().get_context_data( session_info=session_info, title=title, intro_html=intro_html, is_debug=settings.DEBUG, otreehub_url=otreehub_url, **kwargs, )
def get_context_data(self, **kwargs): intro_text = settings.DEMO_PAGE_INTRO_TEXT context = super(DemoIndex, self).get_context_data(**kwargs) session_info = [] for session_config in SESSION_CONFIGS_DICT.values(): session_info.append( { 'name': session_config['name'], 'display_name': session_config['display_name'], 'url': reverse( 'CreateDemoSession', args=(session_config['name'],) ), 'num_demo_participants': session_config[ 'num_demo_participants' ] } ) context.update({ 'session_info': session_info, 'intro_text': intro_text, 'is_debug': settings.DEBUG, }) return context
def get_context_data(self, **kwargs): intro_text = settings.DEMO_PAGE_INTRO_TEXT context = super(DemoIndex, self).get_context_data(**kwargs) session_info = [] for session_config in SESSION_CONFIGS_DICT.values(): session_info.append( { 'name': session_config['name'], 'display_name': session_config['display_name'], 'url': reverse( 'create_demo_session', args=(session_config['name'],) ), 'num_demo_participants': session_config[ 'num_demo_participants' ] } ) context.update({ 'session_info': session_info, 'intro_text': intro_text, 'is_debug': settings.DEBUG }) return context
def get_context_data(self, **kwargs): x = super().get_context_data( configs=SESSION_CONFIGS_DICT.values(), **kwargs, ) return x
def get_context_data(self, **kwargs): title = getattr(settings, 'DEMO_PAGE_TITLE', 'Demo') intro_html = (getattr(settings, 'DEMO_PAGE_INTRO_HTML', '') or getattr(settings, 'DEMO_PAGE_INTRO_TEXT', '')) context = super(DemoIndex, self).get_context_data(**kwargs) session_info = [] for session_config in SESSION_CONFIGS_DICT.values(): session_info.append({ 'name': session_config['name'], 'display_name': session_config['display_name'], 'url': reverse('CreateDemoSession', args=(session_config['name'], )), 'num_demo_participants': session_config['num_demo_participants'] }) context.update({ 'session_info': session_info, 'title': title, 'intro_html': intro_html, 'is_debug': settings.DEBUG, }) return context
def run_all_bots_for_session_config(session_config_name, num_participants, export_path): """ this means all test cases are in 1 big test case. so if 1 fails, the others will not get run. """ if session_config_name: session_config_names = [session_config_name] else: session_config_names = SESSION_CONFIGS_DICT.keys() for config_name in session_config_names: try: config = SESSION_CONFIGS_DICT[config_name] except KeyError: # important to alert the user, since people might be trying to enter app names. msg = f"No session config with name '{config_name}'." raise Exception(msg) from None num_bot_cases = config.get_num_bot_cases() for case_number in range(num_bot_cases): logger.info("Creating '{}' session (test case {})".format( config_name, case_number)) session = otree.session.create_session( session_config_name=config_name, num_participants=(num_participants or config['num_demo_participants']), ) session_id = session.id run_bots(session_id, case_number=case_number) logger.info('Bots completed session') if export_path: now = datetime.datetime.now() if export_path == AUTO_NAME_BOTS_EXPORT_FOLDER: # oTree convention to prefix __temp all temp folders. export_path = now.strftime( '__temp_bots_%b%d_%Hh%Mm%S.%f')[:-5] + 's' os.makedirs(export_path, exist_ok=True) for app in settings.OTREE_APPS: model_module = otree.common.get_models_module(app) if model_module.Player.objects_exists(): fpath = Path(export_path, "{}.csv".format(app)) with fpath.open("w", encoding="utf8") as fp: otree.export.export_app(app, fp) fpath = Path(export_path, "all_apps_wide.csv") with fpath.open("w", encoding="utf8") as fp: otree.export.export_wide(fp) logger.info('Exported CSV to folder "{}"'.format(export_path)) else: logger.info('Tip: Run this command with the --export flag' ' to save the data generated by bots.')
def get_context_data(self, **kwargs): return super().get_context_data( configs=SESSION_CONFIGS_DICT.values(), participant_urls=self.room.get_participant_urls(self.request), room_wide_url=self.room.get_room_wide_url(self.request), room=self.room, form=CreateSessionForm(room_name=self.room_name), collapse_links=True, **kwargs)
def get_context_data(self, **kwargs): x = super().get_context_data( configs=SESSION_CONFIGS_DICT.values(), # splinter makes request.GET.get('mturk') == ['1\\'] # no idea why # so just see if it's non-empty **kwargs, ) return x
class CreateSessionForm(wtforms.Form): session_configs = SESSION_CONFIGS_DICT.values() session_config_choices = [(s['name'], s['display_name']) for s in session_configs] session_config = wtforms.SelectField( choices=session_config_choices, validators=validators_required, render_kw=dict({'class': 'form-select'}), ) num_participants = wtforms.IntegerField( validators=[ wtvalidators.DataRequired(), wtvalidators.NumberRange(min=1) ], render_kw={ 'autofocus': True, 'class': 'form-control w-auto' }, ) # too much weirdness with BooleanField and 'y' # so we render manually # it's a booleanfield so its default value will be 'y', # but it's a hidden widget that we are passing to the server # through .serializeArray, so we need to filter out is_mturk = wtforms.BooleanField() room_name = wtforms.StringField(widget=wtwidgets.HiddenInput()) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.is_mturk.object_data: label = "Number of MTurk workers (assignments)" description = ( 'Since workers can return an assignment or drop out, ' 'some "spare" participants will be created: ' f'the oTree session will have {settings.MTURK_NUM_PARTICIPANTS_MULTIPLE} ' 'times more participant objects than the number you enter here.' ) else: label = "Number of participants" description = '' self.num_participants.label = label self.num_participants.description = description def validate(self): if not super().validate(): return False config = SESSION_CONFIGS_DICT[self.session_config.data] lcm = config.get_lcm() if self.num_participants.data % lcm: self.num_participants.errors.append( 'Please enter a valid number of participants.') return not bool(self.errors)
def test_all_bots_for_session_config( session_config_name, num_participants, export_path): """ this means all configs and test cases are in 1 big test case. so if 1 fails, the others will not get run. to separate them, we would need to move some of this code to pytest_generate_tests in conftest.py """ if session_config_name: session_config_names = [session_config_name] else: session_config_names = SESSION_CONFIGS_DICT.keys() for config_name in session_config_names: try: config = SESSION_CONFIGS_DICT[config_name] except KeyError: # important to alert the user, since people might be trying to enter app names. raise Exception(f"No session config with name '{config_name}'.") from None bot_modules = [f'{app_name}.tests' for app_name in config['app_sequence']] pytest.register_assert_rewrite(*bot_modules) num_bot_cases = config.get_num_bot_cases() for case_number in range(num_bot_cases): logger.info("Creating '{}' session (test case {})".format( config_name, case_number)) session = otree.session.create_session( session_config_name=config_name, num_participants=(num_participants or config['num_demo_participants']), ) run_bots(session, case_number=case_number) logger.info('Bots completed session') if export_path: now = datetime.datetime.now() if export_path == AUTO_NAME_BOTS_EXPORT_FOLDER: # oTree convention to prefix __temp all temp folders. export_path = now.strftime('__temp_bots_%b%d_%Hh%Mm%S.%f')[:-5] + 's' os.makedirs(export_path, exist_ok=True) for app in settings.INSTALLED_OTREE_APPS: model_module = otree.common_internal.get_models_module(app) if model_module.Player.objects.exists(): fpath = Path(export_path, "{}.csv".format(app)) with fpath.open("w", encoding="utf8") as fp: otree.export.export_app(app, fp, file_extension='csv') fpath = Path(export_path, "all_apps_wide.csv") with fpath.open("w", encoding="utf8") as fp: otree.export.export_wide(fp, 'csv') logger.info('Exported CSV to folder "{}"'.format(export_path))
def get_context_data(self, **kwargs): x = super().get_context_data( configs=SESSION_CONFIGS_DICT.values(), # splinter makes request.GET.get('mturk') == ['1\\'] # no idea why # so just see if it's non-empty form=CreateSessionForm(is_mturk=bool(self.request.GET.get('is_mturk'))), **kwargs, ) return x
class CreateSessionForm(forms.Form): session_configs = SESSION_CONFIGS_DICT.values() session_config_choices = ( # use '' instead of None. '' seems to immediately invalidate the choice, # rather than None which seems to be coerced to 'None'. [('', '-----')] + [(s['name'], s['display_name']) for s in session_configs]) session_config = forms.ChoiceField( choices=session_config_choices, required=True) num_participants = forms.IntegerField(required=False) is_mturk = forms.BooleanField( widget=widgets.HiddenInput, initial=False, required=False ) room_name = forms.CharField( initial=None, widget=widgets.HiddenInput, required=False ) def __init__(self, *args, is_mturk=False, room_name=None, **kwargs): super().__init__(*args, **kwargs) self.fields['room_name'].initial = room_name if is_mturk: self.fields['is_mturk'].initial = True self.fields['num_participants'].label = "Number of MTurk workers (assignments)" self.fields['num_participants'].help_text = ( 'Since workers can return an assignment or drop out, ' 'some "spare" participants will be created: ' f'the oTree session will have {settings.MTURK_NUM_PARTICIPANTS_MULTIPLE}' '{} times more participant objects than the number you enter here.' ) else: self.fields['num_participants'].label = "Number of participants" def clean(self): super().clean() if self.errors: return session_config_name = self.cleaned_data['session_config'] config = SESSION_CONFIGS_DICT[session_config_name] lcm = config.get_lcm() num_participants = self.cleaned_data.get('num_participants') if num_participants is None or num_participants % lcm: raise forms.ValidationError( 'Please enter a valid number of participants.' )
def run(self): options = self.options self.check_browser() self.set_urls() self.client = requests.session() self.ping_server() self.server_configuration_check() sessions_to_create = [] if options["session_config_name"]: session_config_name = options["session_config_name"] if session_config_name not in SESSION_CONFIGS_DICT: raise ValueError( 'No session config named "{}"'.format( session_config_name) ) session_config_names = [session_config_name] else: # default to all session configs session_config_names = SESSION_CONFIGS_DICT.keys() self.max_name_length = max( len(config_name) for config_name in session_config_names ) for session_config_name in session_config_names: session_config = SESSION_CONFIGS_DICT[session_config_name] num_bot_cases = session_config.get_num_bot_cases() for bot_case_number in range(num_bot_cases): num_participants = (options.get('num_participants') or session_config['num_demo_participants']) sessions_to_create.append({ 'session_config_name': session_config_name, 'num_participants': num_participants, 'bot_case_number': bot_case_number, }) total_time_spent = 0 # run in a separate loop, because we want to validate upfront # that the session configs are valid, etc, # rather than the command failing halfway through for session_to_create in sessions_to_create: total_time_spent += self.run_session(**session_to_create) print('Total: {} seconds'.format( round(total_time_spent, 1) ))
def run(self): options = self.options self.check_browser() self.set_urls() self.client = requests.session() self.ping_server() self.server_configuration_check() sessions_to_create = [] if options["session_config_name"]: session_config_name = options["session_config_name"] if session_config_name not in SESSION_CONFIGS_DICT: raise ValueError( 'No session config named "{}"'.format( session_config_name) ) session_config_names = [session_config_name] else: # default to all session configs session_config_names = SESSION_CONFIGS_DICT.keys() self.max_name_length = max( len(config_name) for config_name in session_config_names ) for session_config_name in session_config_names: session_config = SESSION_CONFIGS_DICT[session_config_name] num_bot_cases = session_config.get_num_bot_cases() for case_number in range(num_bot_cases): num_participants = (options.get('num_participants') or session_config['num_demo_participants']) sessions_to_create.append({ 'session_config_name': session_config_name, 'num_participants': num_participants, 'case_number': case_number, }) total_time_spent = 0 # run in a separate loop, because we want to validate upfront # that the session configs are valid, etc, # rather than the command failing halfway through for session_to_create in sessions_to_create: total_time_spent += self.run_session(**session_to_create) print('Total: {} seconds'.format( round(total_time_spent, 1) ))
def post_receive_json(self, form_data: dict): session_config_name = form_data['session_config'] config = SESSION_CONFIGS_DICT.get(session_config_name) if not config: msg = f'Session config "{session_config_name}" does not exist.' self.send_json({'validation_errors': msg}) return num_participants = config['num_demo_participants'] use_browser_bots = config.get('use_browser_bots', False) self.create_session_then_send_start_link( session_config_name=session_config_name, use_browser_bots=use_browser_bots, num_participants=num_participants, is_demo=True)
class CreateSessionForm(forms.Form): session_configs = SESSION_CONFIGS_DICT.values() session_config_choices = ( # use '' instead of None. '' seems to immediately invalidate the choice, # rather than None which seems to be coerced to 'None'. [('', '-----')] + [(s['name'], s['display_name']) for s in session_configs]) session_config = forms.ChoiceField( choices=session_config_choices, required=True) num_participants = forms.IntegerField() def __init__(self, *args, **kwargs): for_mturk = kwargs.pop('for_mturk') super().__init__(*args, **kwargs) if for_mturk: self.fields['num_participants'].label = "Number of MTurk workers" self.fields['num_participants'].help_text = ( 'Since workers can return the HIT or drop out, ' 'some "spare" participants will be created: ' 'the oTree session will have ' '{} times more participants than the MTurk HIT. ' 'The number you enter in this field is number of ' 'workers required for your HIT.'.format( settings.MTURK_NUM_PARTICIPANTS_MULTIPLE ) ) else: self.fields['num_participants'].label = "Number of participants" def clean_num_participants(self): session_config_name = self.cleaned_data.get('session_config') # I think when this is checked, it's possible that basic validation # for session_config_name was not done yet. # when I tested it was None # but maybe it could also be the empty string because that's what's # explicitly put above. if session_config_name: lcm = SESSION_CONFIGS_DICT[session_config_name].get_lcm() num_participants = self.cleaned_data['num_participants'] if num_participants % lcm: raise forms.ValidationError( 'Please enter a valid number of participants.' ) return num_participants
def run(self): self.check_browser() self.set_urls() self.client = requests_session() self.client.headers.update({'otree-rest-key': REST_KEY}) sessions_to_create = [] session_config_name = self.session_config_name if session_config_name: if session_config_name not in SESSION_CONFIGS_DICT: msg = 'No session config named "{}"'.format(session_config_name) raise ValueError(msg) session_config_names = [session_config_name] else: # default to all session configs session_config_names = SESSION_CONFIGS_DICT.keys() self.max_name_length = max( len(config_name) for config_name in session_config_names ) for session_config_name in session_config_names: session_config = SESSION_CONFIGS_DICT[session_config_name] num_bot_cases = session_config.get_num_bot_cases() for case_number in range(num_bot_cases): num_participants = ( self.num_participants or session_config['num_demo_participants'] ) sessions_to_create.append( { 'session_config_name': session_config_name, 'num_participants': num_participants, 'case_number': case_number, } ) total_time_spent = 0 # run in a separate loop, because we want to validate upfront # that the session configs are valid, etc, # rather than the command failing halfway through for session_to_create in sessions_to_create: total_time_spent += self.run_session(**session_to_create) print('Total: {} seconds'.format(round(total_time_spent, 1)))
def render_to_response(self, context): session = Session.objects.get(code=self.kwargs['session_code']) events_by_app_name_then_group = defaultdict(lambda: {}) for session_config in SESSION_CONFIGS_DICT.values(): app_name = session_config['name'] try: groups_query = getattr(session, app_name + '_group') except AttributeError: continue groups = list(groups_query.all()) if groups: for group in groups: events = Event.objects.filter(group_pk=group.pk) events_by_app_name_then_group[app_name][group.pk] = [ e.message for e in events ] return JsonResponse(events_by_app_name_then_group, safe=False)
def pytest_generate_tests(metafunc): # if the test function has a parameter called session_config_name if 'session_config_name' in metafunc.fixturenames: option = metafunc.config.option session_config_name = option.session_config_name if session_config_name: session_config_names = [session_config_name] else: session_config_names = SESSION_CONFIGS_DICT.keys() num_participants = option.num_participants if num_participants: num_participants = int(num_participants) params = [[name, num_participants, False] for name in session_config_names] if option.preserve_data and len(params) >= 1: params[-1][2] = True metafunc.parametrize("session_config_name,num_participants,run_export", params)
def pytest_generate_tests(metafunc): # if the test function has a parameter called session_config_name if 'session_config_name' in metafunc.fixturenames: option = metafunc.config.option session_config_name = option.session_config_name if session_config_name: session_config_names = [session_config_name] else: session_config_names = SESSION_CONFIGS_DICT.keys() num_participants = option.num_participants if num_participants: num_participants = int(num_participants) preserve_data = option.preserve_data params = [[name, num_participants, preserve_data] for name in session_config_names] metafunc.parametrize( "session_config_name,num_participants,preserve_data", params)
def pytest_generate_tests(metafunc): # if the test function has a parameter called session_config_name if 'session_config_name' in metafunc.fixturenames: option = metafunc.config.option session_config_name = option.session_config_name if session_config_name: session_config_names = [session_config_name] else: session_config_names = SESSION_CONFIGS_DICT.keys() num_participants = option.num_participants if num_participants: num_participants = int(num_participants) params = [ [name, num_participants, False] for name in session_config_names] if option.preserve_data and len(params) >= 1: params[-1][2] = True metafunc.parametrize( "session_config_name,num_participants,run_export", params)
class CreateSessionForm(forms.Form): session_configs = SESSION_CONFIGS_DICT.values() session_config_choices = ( [('', '-----')] + [(s['name'], s['display_name']) for s in session_configs]) session_config = forms.ChoiceField( choices=session_config_choices, required=True) num_participants = forms.IntegerField() def __init__(self, *args, **kwargs): for_mturk = kwargs.pop('for_mturk') super(CreateSessionForm, self).__init__(*args, **kwargs) if for_mturk: self.fields['num_participants'].label = "Number of workers" self.fields['num_participants'].help_text = ( 'Since workers can return the hit or drop out ' '"spare" participants will be created. Namely server will ' 'have %s times more participants than MTurk HIT. ' 'The number you enter in this field is number of ' 'workers required for your HIT.' % settings.MTURK_NUM_PARTICIPANTS_MULTIPLE ) else: self.fields['num_participants'].label = "Number of participants" def clean_num_participants(self): session_config_name = self.cleaned_data.get('session_config') # We must check for an empty string in case validation is not run if session_config_name != '': lcm = SESSION_CONFIGS_DICT[session_config_name].get_lcm() num_participants = self.cleaned_data['num_participants'] if num_participants % lcm: raise forms.ValidationError( 'Please enter a valid number of participants.' ) return num_participants
def handle(self, **options): session_config_names = options["session_name"] if not session_config_names: # default to all session configs session_config_names = SESSION_CONFIGS_DICT.keys() if options['verbosity'] == 0: level = logging.ERROR elif options['verbosity'] == 1: level = logging.WARNING elif options['verbosity'] == 2: level = logging.INFO else: # 3 level = logging.DEBUG options['verbosity'] = (options['verbosity'] if options['verbosity'] > 2 else 1) logging.basicConfig(level=level) logging.getLogger("otree").setLevel(level) runner.logger.setLevel(level) client.logger.setLevel(level) export_path = options["export"] or options["save"] preserve_data = bool(export_path) test_runner = runner.OTreeExperimentTestRunner(**options) coverage = options["coverage"] if coverage: with runner.covering(session_config_names) as coverage_report: failures, data = test_runner.run_tests( session_config_names, preserve_data=preserve_data) else: failures, data = test_runner.run_tests(session_config_names, preserve_data=preserve_data) if coverage: logger.info("Coverage Report") if coverage in [COVERAGE_CONSOLE, COVERAGE_ALL]: coverage_report.report() if coverage in [COVERAGE_HTML, COVERAGE_ALL]: html_coverage_results_dir = '_coverage_results' coverage_report.html_report( directory=html_coverage_results_dir) msg = ("See '{}/index.html' for detailed results." ).format(html_coverage_results_dir) logger.info(msg) if preserve_data: now = datetime.datetime.now() if export_path == 'auto_name': export_path = now.strftime('_bots_%b%d_%Hh%Mm%S.%f')[:-5] + 's' if os.path.isdir(export_path): msg = "Directory '{}' already exists".format(export_path) raise IOError(msg) os.makedirs(export_path) metadata = dict(options) metadata.update({ "timestamp": now.isoformat(), "versions": otree_and_django_version(), "failures": failures, "error": bool(failures) }) sizes = {} for session_name, session_data in data.items(): session_data = session_data or "" sizes[session_name] = len(session_data.splitlines()) fname = "{}.csv".format(session_name) fpath = os.path.join(export_path, fname) with codecs.open(fpath, "w", encoding="utf8") as fp: fp.write(session_data) metainfo = "\n".join( ["{}: {}".format(k, v) for k, v in metadata.items()] + ["sizes:"] + ["\t{}: {}".format(k, v) for k, v in sizes.items()] + [""]) fpath = os.path.join(export_path, "meta.txt") with codecs.open(fpath, "w", encoding="utf8") as fp: fp.write(metainfo) logger.info('Exported CSV to folder "{}"'.format(export_path)) else: logger.info('Tip: Run this command with the --export flag' ' to save the data generated by bots.') if failures: sys.exit(bool(failures))
url_name = 'redwood_debug' url_pattern = r'^redwood/debug/session/(?P<session_code>[a-zA-Z0-9_-]+)/$' template_name = 'otree_redwood/Debug.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['stats'] = stats.items() channel_layer = channels.asgi.get_channel_layer() if 'statistics' in channel_layer.extensions: context['global_channel_stats'] = channel_layer.global_statistics() context['connected_participants'] = Connection.objects.all() context['session_code'] = self.kwargs['session_code'] return context app_specific_exports = [] for session_config in SESSION_CONFIGS_DICT.values(): app_name = session_config['name'] dotted_path = app_name + '.views' display_name = session_config['display_name'] try: module = import_module(dotted_path) except ImportError: continue table_fn = getattr(module, 'get_output_table', None) header_fn = getattr(module, 'get_output_table_header', None) if table_fn and header_fn: app_specific_exports.append( AppSpecificExportCSV(app_name, display_name, table_fn, header_fn))
def get_context_data(self, **kwargs): kwargs['configs'] = SESSION_CONFIGS_DICT.values() return super().get_context_data(**kwargs)
def handle(self, **options): session_config_names = options["session_name"] if not session_config_names: # default to all session configs session_config_names = SESSION_CONFIGS_DICT.keys() if options['verbosity'] == 0: level = logging.ERROR elif options['verbosity'] == 1: level = logging.WARNING elif options['verbosity'] == 2: level = logging.INFO else: # 3 level = logging.DEBUG options['verbosity'] = ( options['verbosity'] if options['verbosity'] > 2 else 1) logging.basicConfig(level=level) logging.getLogger("otree").setLevel(level) runner.logger.setLevel(level) client.logger.setLevel(level) export_path = options["export"] or options["save"] preserve_data = bool(export_path) test_runner = runner.OTreeExperimentTestRunner(**options) coverage = options["coverage"] if coverage: with runner.covering(session_config_names) as coverage_report: failures, data = test_runner.run_tests( session_config_names, preserve_data=preserve_data) else: failures, data = test_runner.run_tests( session_config_names, preserve_data=preserve_data) if coverage: logger.info("Coverage Report") if coverage in [COVERAGE_CONSOLE, COVERAGE_ALL]: coverage_report.report() if coverage in [COVERAGE_HTML, COVERAGE_ALL]: html_coverage_results_dir = '_coverage_results' coverage_report.html_report( directory=html_coverage_results_dir) msg = ("See '{}/index.html' for detailed results.").format( html_coverage_results_dir) logger.info(msg) if preserve_data: now = datetime.datetime.now() if export_path == 'auto_name': export_path = now.strftime('_bots_%b%d_%Hh%Mm%S.%f')[:-5] + 's' if os.path.isdir(export_path): msg = "Directory '{}' already exists".format(export_path) raise IOError(msg) os.makedirs(export_path) metadata = dict(options) metadata.update({ "timestamp": now.isoformat(), "versions": otree_and_django_version(), "failures": failures, "error": bool(failures)}) sizes = {} for session_name, session_data in data.items(): session_data = session_data or "" sizes[session_name] = len(session_data.splitlines()) fname = "{}.csv".format(session_name) fpath = os.path.join(export_path, fname) with codecs.open(fpath, "w", encoding="utf8") as fp: fp.write(session_data) metainfo = "\n".join( ["{}: {}".format(k, v) for k, v in metadata.items()] + ["sizes:"] + ["\t{}: {}".format(k, v) for k, v in sizes.items()] + [""]) fpath = os.path.join(export_path, "meta.txt") with codecs.open(fpath, "w", encoding="utf8") as fp: fp.write(metainfo) logger.info('Exported CSV to folder "{}"'.format(export_path)) else: logger.info('Tip: Run this command with the --export flag' ' to save the data generated by bots.') if failures: sys.exit(bool(failures))
def get_context_data(self, **kwargs): session_config_summaries = [ session_config.get_info() for session_config in SESSION_CONFIGS_DICT.values()] kwargs.update({'session_config_summaries': session_config_summaries}) return super(CreateSession, self).get_context_data(**kwargs)