def expand_choices(tree, params, include_path): rows = [] for command, *args in tree: if command == "#": continue if command == "@": if args[0] == "include": rows.extend( execute_include(filename=args[1], args=args[2:], params=copy.deepcopy(params), include_path=include_path)) else: raise NameError(f"Unknown command: @{args[0]}.") elif command == "list": list_params = merge_params(params, {"list_name": args[0]}) list_params.update(args_to_params(args[1])) rows.extend(expand_choices(args[2], list_params, include_path)) else: rows.append(compile_choice(command, args, copy.deepcopy(params))) return rows
def execute_include(filename, args, params, include_path): """Generate entries for the 'choices' worksheet based on an @include command.""" include_path, filename = new_path_and_filename(include_path, filename) include_macros = args_to_params(args) with open(filename) as f: return expand_choices( parse_choices().parseString(substitute_macros( f.read(), include_macros), parseAll=True).asList(), params, include_path)
def execute_form_settings(form_id, form_version, form_title, args): "Generate the 'settings' worksheet based on a @form command." form_settings = {"form_title": form_title, "form_id": form_id} form_settings.update( args_to_params(args)) if form_version == "auto": form_settings["version"] = ( datetime.datetime.utcnow() .strftime("%y%m%d%H%M")) else: form_settings["version"] = form_version return form_settings
def execute_choices(filename, args, include_path): "Generate a choice list based on a @choices command." include_path, filename = new_path_and_filename( include_path, filename) choices_macros = args_to_params(args) with open(filename) as f: return expand_choices( parse_choices().parseString( substitute_macros( f.read(), choices_macros), parseAll=True).asList(), dict(), include_path)
def compile_question(name, args, params): "Compile a question for the 'survey' worksheet." question = {"name": name, "type": " ".join(str(word) for word in args[0])} if len(args) % 2 == 1: question_params = args[1:] else: if question["type"] == "calculate": question["calculation"] = args[1] else: question["label"] = args[1] question_params = args[2:] question.update( merge_params( params, args_to_params(question_params))) return question
def execute_include(filename, args, params, include_path): """Generate 'survey' and 'choices' worksheets based on an @include command.""" include_path, filename = new_path_and_filename( include_path, filename) include_macros = args_to_params(args) with open(filename) as f: try: include_tree = parse_survey().parseString( preprocess_indent( substitute_macros( f.read(), include_macros)), parseAll=True).asList() except pp.ParseException: raise ValueError( f"Error when parsing {filename}.") return expand_survey(include_tree, params, include_path)
def compile_choice(value, args, params): choice = {"value": value, "label": args[0]} choice.update(merge_params(params, args_to_params(args[1:]))) return choice
def expand_survey(tree, params, include_path): rows = [] choices = [] settings = None for command, *args in tree: if command == "#": continue if command == "@": if args[0] == "form": settings = [ execute_form_settings( form_id=args[1], form_version=args[2], form_title=args[3], args=args[4:])] elif args[0] == "choices": choices.extend( execute_choices( filename=args[1], args=args[2:], include_path=include_path)) elif args[0] == "include": included_survey = execute_include( filename=args[1], args=args[2:], params=copy.deepcopy(params), include_path=include_path) rows.extend(included_survey.survey) choices.extend(included_survey.choices) elif args[0] == "required": params["required"] = args[1] else: raise NameError(f"Unknown command: @{args[0]}.") elif command == "if": if_params = merge_params( params, {"relevance": if_cond_to_relevance(args[0])}) expanded = expand_survey( args[1], if_params, include_path) rows.extend(expanded.survey) elif command in ("group", "repeat"): group_name = args[0][0] group_def = { "type": f"begin {command}", "name": group_name } if len(args[0]) > 1: group_def["label"] = args[0][1] group_params = merge_params( params, args_to_params(args[1])) rows.append({**group_def, **group_params}) for param_name in ("relevance", "appearance"): if param_name in group_params: del group_params[param_name] expanded = expand_survey( args[2], group_params, include_path) rows.extend(expanded.survey) rows.append( {"type": f"end {command}", "name": group_name}) else: rows.append( compile_question( command, args, copy.deepcopy(params))) return namedtuple("Survey", ("survey", "choices", "settings"))( survey=rows, choices=choices, settings=settings)