|
| 1 | +import typing as tp |
| 2 | +import pathlib |
| 3 | +import datetime |
| 4 | +import itertools |
| 5 | + |
| 6 | +from data_exporting import format_timestamp_for_file, save_csv, OutputSheet |
| 7 | + |
| 8 | +"""Support for additional outputs used by the Multiple Choice Test Analysis software.""" |
| 9 | + |
| 10 | +def transform_and_save_mcta_output(answers_results: OutputSheet, |
| 11 | + keys_results: OutputSheet, |
| 12 | + files_timestamp: datetime, |
| 13 | + output_folder: pathlib.Path): |
| 14 | + """Generate and save files that are specific to a downstream Multiple Choice Test Analysis |
| 15 | + software. The format of these files is dependend on the downstream software, so they are not |
| 16 | + consistent with the rest of the output.""" |
| 17 | + create_keys_files(keys_results, output_folder, files_timestamp) |
| 18 | + create_answers_files(answers_results, output_folder, files_timestamp) |
| 19 | + |
| 20 | + |
| 21 | +def create_keys_files(keys_results: OutputSheet, output_folder: pathlib.Path, files_timestamp: datetime): |
| 22 | + """Create the key files for the Multiple Choice Test Analysis software. |
| 23 | +
|
| 24 | + Params: |
| 25 | + keys_results: The results of the keys file. |
| 26 | + output_folder: The folder to save the files to. |
| 27 | + files_timestamp: The timestamp to use for the files. |
| 28 | + """ |
| 29 | + form_code_col = keys_results.form_code_column_index |
| 30 | + |
| 31 | + for row in keys_results.data[1:]: |
| 32 | + code = row[form_code_col] |
| 33 | + csv_data = build_key_csv(row[keys_results.first_question_column_index:]) |
| 34 | + save_mcta_csv(csv_data, output_folder, f"{code}_key", files_timestamp) |
| 35 | + |
| 36 | + |
| 37 | +def create_answers_files(answers_results: OutputSheet, |
| 38 | + output_folder: pathlib.Path, |
| 39 | + files_timestamp: datetime): |
| 40 | + """Create the answer files for the Multiple Choice Test Analysis software. |
| 41 | +
|
| 42 | + Params: |
| 43 | + answers_results: The results of the answers file. |
| 44 | + output_folder: The folder to save the files to. |
| 45 | + files_timestamp: The timestamp to use for the files. |
| 46 | + """ |
| 47 | + form_code_col = answers_results.form_code_column_index |
| 48 | + first_question_col = answers_results.first_question_column_index |
| 49 | + |
| 50 | + # Preserve the original index for naming students anonymously |
| 51 | + # List of tuples of (form code, original index, answers) |
| 52 | + answers_with_form_code = [(row[form_code_col], i, row[first_question_col:]) for (i, row) in enumerate(answers_results.data[1:])] |
| 53 | + |
| 54 | + # groupby requires sorted input |
| 55 | + sorted_by_code = sorted(answers_with_form_code, key=lambda x: x[0]) |
| 56 | + grouped_by_code = itertools.groupby(sorted_by_code, key=lambda x: x[0]) |
| 57 | + |
| 58 | + # Generate one output file for each form code in the answers data |
| 59 | + for code, group in grouped_by_code: |
| 60 | + group_data = [(original_index, answers) for (_, original_index, answers) in group] |
| 61 | + csv_data = build_answers_csv(group_data) |
| 62 | + # Test form code can be in [A|B] form if student selects A and B. The [|] are not safe for filename. |
| 63 | + file_safe_code = code.replace("[", "").replace("]", "").replace("|", "") |
| 64 | + save_mcta_csv(csv_data, output_folder, f"{file_safe_code}_results", files_timestamp) |
| 65 | + |
| 66 | + |
| 67 | +def build_key_csv(answers: tp.List[str]) -> tp.List[tp.List[str]]: |
| 68 | + """Build the CSV data for a key file. Each key outputs a separate pair of key and answer files. |
| 69 | +
|
| 70 | + Params: |
| 71 | + answers: All of the answers for this form code, in order. |
| 72 | + """ |
| 73 | + header = ["", "Answer", "Title", "Concept"] |
| 74 | + data = [[f"Q{i}", x, f"Q{i}", "unknown"] for i, x in enumerate(answers, 1)] |
| 75 | + return [header] + data |
| 76 | + |
| 77 | + |
| 78 | +def build_answers_csv(data: tp.List[tp.Tuple[int, tp.List[str]]]) -> tp.List[tp.List[str]]: |
| 79 | + """Build the CSV data for an answers file. Should be called once for each form code. |
| 80 | +
|
| 81 | + Params: |
| 82 | + data: The data to save into the file. A list of rows, where each row represents a student. |
| 83 | + Each row is a tuple of the student's original index (for naming) and the list of |
| 84 | + answers. |
| 85 | + """ |
| 86 | + header = [""] + [f"Q{i + 1}" for i in range(0, len(data[0][1]))] |
| 87 | + rows = [[f"Student{i}"] + answers for (i, answers) in data] |
| 88 | + return [header] + rows |
| 89 | + |
| 90 | + |
| 91 | +def save_mcta_csv(data: tp.List[tp.List[str]], |
| 92 | + path: pathlib.PurePath, |
| 93 | + basefilename: str, |
| 94 | + timestamp: datetime): |
| 95 | + filename = path / f"{format_timestamp_for_file(timestamp)}__mcta_{basefilename}.csv" |
| 96 | + save_csv(data, filename) |
0 commit comments