示例#1
0
def establish_key_dict(
    answer_keys: data_exporting.OutputSheet
) -> typing.Dict[str, typing.List[str]]:
    """Takes the matrix of answer keys and transforms it into a dictionary that
    maps the test form codes to the list of correct answers.

    Treats the answer_keys data naively by assuming the following:
        * The column with the form codes comes before the answer columns.
        * The first answer column is named "Q1".
        * The answers are all in order.
    If these are wrong, the results will be incorrect.

    Also note: the returned list of answers matches the order of the questions,
    but the questions are named "Q1" through "Q75" and the answers are in indexes
    0 through 74.
    """
    keys = answer_keys.data
    form_code_column_name = data_exporting.COLUMN_NAMES[
        grid_info.Field.TEST_FORM_CODE]
    try:
        # Get the index of the column that had the test form codes
        form_code_index = list_utils.find_index(keys[0], form_code_column_name)
        # After the test form codes column, search for the 1st question column
        # index
        answers_start_index = list_utils.find_index(
            keys[0][form_code_index + 1:], "Q1") + form_code_index + 1
    except StopIteration:
        raise ValueError(
            "Invalid key matrix passed to scoring functions. Test form code column must be prior to answers columns, which must be named 'Q1' through 'QN'."
        )

    return {
        key[form_code_index]: key[answers_start_index:]
        for key in keys[1:]
    }
示例#2
0
def verify_answer_key_sheet(file_path: pathlib.Path) -> bool:
    try:
        with open(str(file_path), newline='') as file:
            reader = csv.reader(file)
            keys_column_name = data_exporting.COLUMN_NAMES[
                grid_info.Field.TEST_FORM_CODE]
            names = next(reader)
            keys_column_name_index = list_utils.find_index(
                names, keys_column_name)
            list_utils.find_index(names[keys_column_name_index:], "Q1")
        return True
    except Exception:
        return False
示例#3
0
    def reorder(self, arrangement_file: pathlib.Path):
        """Reorder the sheet based on an arrangement map file.

        Raises TypeError if invalid arrangement file.

        Results will have empty form code index."""
        # TODO: Validate arrangement file.
        # order_map will be a dict matching form code keys to a list where the
        # new index of question `i` in `key` is `order_map[key][i]`
        order_map: tp.Dict[str, tp.List[int]] = {}
        validate_order_map(order_map, self.num_questions)

        with open(str(arrangement_file), 'r', newline='') as file:
            reader = csv.reader(file)
            names = list_utils.strip_all(next(reader))
            form_code_index = list_utils.find_index(
                names, COLUMN_NAMES[Field.TEST_FORM_CODE])
            first_answer_index = list_utils.find_index(names, "Q1")
            for form in reader:
                stripped_form = list_utils.strip_all(form)
                form_code = stripped_form[form_code_index]
                to_order_zero_ind = [
                    int(n) - 1 for n in stripped_form[first_answer_index:]
                ]
                order_map[form_code] = to_order_zero_ind

        sheet_form_code_index = list_utils.find_index(
            self.data[0], COLUMN_NAMES[Field.TEST_FORM_CODE])
        sheet_first_answer_index = list_utils.find_index(self.data[0], "Q1")
        rearranged = [self.data[0]]

        for row in self.data[1:]:
            original_form_code = row[sheet_form_code_index]
            try:
                order_map[original_form_code]
            except KeyError:
                raise ValueError(
                    f"Arrangement file is missing entry for key '{original_form_code}'."
                )
            else:
                row_reordered = row[:sheet_first_answer_index] + [
                    row[ind + sheet_first_answer_index]
                    for ind in order_map[original_form_code]
                ]
                row_reordered[sheet_form_code_index] = ""
                rearranged.append(row_reordered)

        self.data = rearranged
示例#4
0
def establish_key_dict(answer_keys: data_exporting.OutputSheet
                       ) -> tp.Dict[str, tp.List[str]]:
    """Takes the matrix of answer keys and transforms it into a dictionary that
    maps the test form codes to the list of correct answers.
    Treats the answer_keys data naively by assuming the following:
        * The column with the form codes comes before the answer columns.
        * The first answer column is named "Q1".
        * The answers are all in order.
    If these are wrong, the results will be incorrect.
    Also note: the returned list of answers matches the order of the questions,
    but the questions are named "Q1" through "Q75" and the answers are in indexes
    0 through 74.
    """

    try:
        answers_start_index = list_utils.find_index(answer_keys.data[0], "Q1")
    except StopIteration:
        raise ValueError(
            "Invalid key matrix passed to scoring functions. Answers columns must be named 'Q1' through 'QN'."
        )

    return {
        get_key_form_code(answer_keys, index): key[answers_start_index:]
        for index, key in enumerate(answer_keys.data[1:])
    }
示例#5
0
def score_results(results: data_exporting.OutputSheet,
                  answer_keys: data_exporting.OutputSheet,
                  num_questions: int) -> data_exporting.OutputSheet:
    answers = results.data
    keys = establish_key_dict(answer_keys)
    form_code_column_name = data_exporting.COLUMN_NAMES[
        grid_info.Field.TEST_FORM_CODE]
    form_code_index = list_utils.find_index(answers[0], form_code_column_name)
    answers_start_index = list_utils.find_index(
        answers[0][form_code_index + 1:], "Q1") + form_code_index + 1
    virtual_fields: tp.List[grid_info.RealOrVirtualField] = [
        grid_info.VirtualField.SCORE, grid_info.VirtualField.POINTS
    ]
    columns = results.field_columns + virtual_fields
    scored_results = data_exporting.OutputSheet(columns, num_questions)

    for exam in answers[1:]:  # Skip header row
        fields = {
            k: v
            for k, v in zip(results.field_columns, exam[:answers_start_index])
        }
        form_code = exam[form_code_index]
        try:
            if "*" in keys:
                key = keys["*"]
            else:
                key = keys[form_code]
        except KeyError:
            fields[grid_info.VirtualField.
                   SCORE] = data_exporting.KEY_NOT_FOUND_MESSAGE
            fields[grid_info.VirtualField.
                   POINTS] = data_exporting.KEY_NOT_FOUND_MESSAGE
            scored_answers = []
        else:
            scored_answers = [
                int(actual == correct)
                for actual, correct in zip(exam[answers_start_index:], key)
            ]
            fields[grid_info.VirtualField.SCORE] = str(
                round(math_utils.mean(scored_answers) * 100, 2))
            fields[grid_info.VirtualField.POINTS] = str(sum(scored_answers))
        string_scored_answers = [str(s) for s in scored_answers]
        scored_results.add(fields, string_scored_answers)

    return scored_results
示例#6
0
def save_reordered_version(sheet: OutputSheet, arrangement_file: pathlib.Path,
                           save_path: pathlib.Path, filebasename: str,
                           timestamp: datetime) -> pathlib.PurePath:
    """Reorder the output sheet based on a key arrangement file and save CSV."""
    # order_map will be a dict matching form code keys to a list where the
    # new index of question `i` in `key` is `order_map[key][i]`
    order_map: typing.Dict[str, typing.List[int]] = {}
    with open(str(arrangement_file), 'r', newline='') as file:
        reader = csv.reader(file)
        names = next(reader)
        form_code_index = list_utils.find_index(
            names, COLUMN_NAMES[Field.TEST_FORM_CODE])
        first_answer_index = list_utils.find_index(names, "Q1")
        for form in reader:
            form_code = form[form_code_index]
            to_order_zero_ind = [int(n) - 1 for n in form[first_answer_index:]]
            order_map[form_code] = to_order_zero_ind
    sheet_form_code_index = list_utils.find_index(
        sheet.data[0], COLUMN_NAMES[Field.TEST_FORM_CODE])
    sheet_first_answer_index = list_utils.find_index(sheet.data[0], "Q1")
    sheet_total_score_index = list_utils.find_index(
        sheet.data[0], COLUMN_NAMES[VirtualField.SCORE])
    results = [sheet.data[0]]
    for row in sheet.data[1:]:
        form_code = row[sheet_form_code_index]
        if row[sheet_total_score_index] != KEY_NOT_FOUND_MESSAGE:
            try:
                order_map[form_code]
            except IndexError:
                results.append(row)
            else:
                row_reordered = row[:sheet_first_answer_index] + [
                    row[ind + sheet_first_answer_index]
                    for ind in order_map[form_code]
                ]
                results.append(row_reordered)
        else:
            results.append(row)
    output_path = save_path / f"{format_timestamp_for_file(timestamp)}__{filebasename}.csv"
    with open(str(output_path), 'w+', newline='') as output_file:
        writer = csv.writer(output_file)
        writer.writerows(results)
    return output_path
示例#7
0
def get_key_form_code(answer_keys: data_exporting.OutputSheet,
                      index: int) -> str:
    """Gets the form code of the answer key at the index given, where the first
    answer key has index=0."""
    keys = answer_keys.data
    form_code_column_name = data_exporting.COLUMN_NAMES[
        grid_info.Field.TEST_FORM_CODE]
    try:
        # Get the index of the column that had the test form codes
        form_code_index = list_utils.find_index(keys[0], form_code_column_name)
        return keys[index + 1][form_code_index]
    except StopIteration:
        return "*"
示例#8
0
 def sortByName(self):
     data = self.data[1:]
     col_names = self.data[0]
     try:
         primary_index = list_utils.find_index(
             col_names, COLUMN_NAMES[Field.LAST_NAME])
         secondary_index = list_utils.find_index(
             col_names, COLUMN_NAMES[Field.FIRST_NAME])
         tertiary_index = list_utils.find_index(
             col_names, COLUMN_NAMES[Field.MIDDLE_NAME])
     except StopIteration:
         try:
             primary_index = list_utils.find_index(
                 col_names, COLUMN_NAMES[Field.TEST_FORM_CODE])
             secondary_index = None
             tertiary_index = None
         except StopIteration:
             return
     if tertiary_index is not None:
         data = sorted(data, key=operator.itemgetter(tertiary_index))
     if secondary_index is not None:
         data = sorted(data, key=operator.itemgetter(secondary_index))
     data = sorted(data, key=operator.itemgetter(primary_index))
     self.data = [col_names] + data