mathtext-fastapi / mathtext_fastapi /curriculum_mapper.py
Greg Thompson
Add tests to curriculum mapper functions
a34228d
raw
history blame
No virus
9.45 kB
import numpy as np
import pandas as pd
import re
from pathlib import Path
def read_and_preprocess_spreadsheet(file_name):
""" Creates a pandas dataframe from the curriculum overview spreadsheet """
DATA_DIR = Path(__file__).parent.parent / "mathtext_fastapi" / "data" / file_name
script_df = pd.read_excel(DATA_DIR, engine='openpyxl')
# Ensures the grade level columns are integers instead of floats
script_df.columns = script_df.columns[:2].tolist() + script_df.columns[2:11].astype(int).astype(str).tolist() + script_df.columns[11:].tolist()
script_df.fillna('', inplace=True)
return script_df
def extract_skill_code(skill):
""" Looks within a curricular skill description for its descriptive code
Input
- skill: str - a brief description of a curricular skill
>>> extract_skill_code('A3.3.4 - Solve inequalities')
'A3.3.4'
>>> extract_skill_code('A3.3.2 - Graph linear equations, and identify the x- and y-intercepts or the slope of a line')
'A3.3.2'
"""
pattern = r'[A-Z][0-9]\.\d+\.\d+'
result = re.search(pattern, skill)
return result.group()
def build_horizontal_transitions(script_df):
""" Build a list of transitional relationships within a curricular skill
Inputs
- script_df: pandas dataframe - an overview of the curriculum skills by grade level
Output
- horizontal_transitions: array of arrays - transition data with label, from state, and to state
>>> script_df = read_and_preprocess_spreadsheet('curriculum_framework_for_tests.xlsx')
>>> build_horizontal_transitions(script_df)
[['right', 'N1.1.1_G1', 'N1.1.1_G2'], ['right', 'N1.1.1_G2', 'N1.1.1_G3'], ['right', 'N1.1.1_G3', 'N1.1.1_G4'], ['right', 'N1.1.1_G4', 'N1.1.1_G5'], ['right', 'N1.1.1_G5', 'N1.1.1_G6'], ['left', 'N1.1.1_G6', 'N1.1.1_G5'], ['left', 'N1.1.1_G5', 'N1.1.1_G4'], ['left', 'N1.1.1_G4', 'N1.1.1_G3'], ['left', 'N1.1.1_G3', 'N1.1.1_G2'], ['left', 'N1.1.1_G2', 'N1.1.1_G1'], ['right', 'N1.1.2_G1', 'N1.1.2_G2'], ['right', 'N1.1.2_G2', 'N1.1.2_G3'], ['right', 'N1.1.2_G3', 'N1.1.2_G4'], ['right', 'N1.1.2_G4', 'N1.1.2_G5'], ['right', 'N1.1.2_G5', 'N1.1.2_G6'], ['left', 'N1.1.2_G6', 'N1.1.2_G5'], ['left', 'N1.1.2_G5', 'N1.1.2_G4'], ['left', 'N1.1.2_G4', 'N1.1.2_G3'], ['left', 'N1.1.2_G3', 'N1.1.2_G2'], ['left', 'N1.1.2_G2', 'N1.1.2_G1']]
"""
horizontal_transitions = []
for index, row in script_df.iterrows():
skill_code = extract_skill_code(row['Knowledge or Skill'])
rightward_matches = []
for i in range(9):
# Grade column
current_grade = i+1
if row[current_grade].lower().strip() == 'x':
rightward_matches.append(i)
for match in rightward_matches:
if rightward_matches[-1] != match:
horizontal_transitions.append([
"right",
f"{skill_code}_G{match}",
f"{skill_code}_G{match+1}"
])
leftward_matches = []
for i in reversed(range(9)):
current_grade = i
if row[current_grade].lower().strip() == 'x':
leftward_matches.append(i)
for match in leftward_matches:
if leftward_matches[0] != match:
horizontal_transitions.append([
"left",
f"{skill_code}_G{match}",
f"{skill_code}_G{match-1}"
])
return horizontal_transitions
def gather_all_vertical_matches(script_df):
""" Build a list of transitional relationships within a grade level across skills
Inputs
- script_df: pandas dataframe - an overview of the curriculum skills by grade level
Output
- all_matches: array of arrays - represents skills at each grade level
>>> script_df = read_and_preprocess_spreadsheet('curriculum_framework_for_tests.xlsx')
>>> gather_all_vertical_matches(script_df)
[['N1.1.1', '1'], ['N1.1.2', '1'], ['N1.1.1', '2'], ['N1.1.2', '2'], ['N1.1.1', '3'], ['N1.1.2', '3'], ['N1.1.1', '4'], ['N1.1.2', '4'], ['N1.1.1', '5'], ['N1.1.2', '5'], ['N1.1.1', '6'], ['N1.1.2', '6']]
"""
all_matches = []
columns = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
for column in columns:
for index, value in script_df[column].iteritems():
row_num = index + 1
if value == 'x':
# Extract skill code
skill_code = extract_skill_code(
script_df['Knowledge or Skill'][row_num-1]
)
all_matches.append([skill_code, column])
return all_matches
def build_vertical_transitions(script_df):
""" Build a list of transitional relationships within a grade level across skills
Inputs
- script_df: pandas dataframe - an overview of the curriculum skills by grade level
Output
- vertical_transitions: array of arrays - transition data with label, from state, and to state
>>> script_df = read_and_preprocess_spreadsheet('curriculum_framework_for_tests.xlsx')
>>> build_vertical_transitions(script_df)
[['down', 'N1.1.1_G1', 'N1.1.2_G1'], ['down', 'N1.1.2_G1', 'N1.1.1_G1'], ['down', 'N1.1.1_G2', 'N1.1.2_G2'], ['down', 'N1.1.2_G2', 'N1.1.1_G2'], ['down', 'N1.1.1_G3', 'N1.1.2_G3'], ['down', 'N1.1.2_G3', 'N1.1.1_G3'], ['down', 'N1.1.1_G4', 'N1.1.2_G4'], ['down', 'N1.1.2_G4', 'N1.1.1_G4'], ['down', 'N1.1.1_G5', 'N1.1.2_G5'], ['down', 'N1.1.2_G5', 'N1.1.1_G5'], ['down', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G6', 'N1.1.1_G6'], ['up', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G5', 'N1.1.1_G5'], ['up', 'N1.1.1_G5', 'N1.1.2_G5'], ['up', 'N1.1.2_G4', 'N1.1.1_G4'], ['up', 'N1.1.1_G4', 'N1.1.2_G4'], ['up', 'N1.1.2_G3', 'N1.1.1_G3'], ['up', 'N1.1.1_G3', 'N1.1.2_G3'], ['up', 'N1.1.2_G2', 'N1.1.1_G2'], ['up', 'N1.1.1_G2', 'N1.1.2_G2'], ['up', 'N1.1.2_G1', 'N1.1.1_G1']]
"""
vertical_transitions = []
all_matches = gather_all_vertical_matches(script_df)
# Downward
for index, match in enumerate(all_matches):
skill = match[0]
row_num = match[1]
if all_matches[-1] != match:
vertical_transitions.append([
"down",
f"{skill}_G{row_num}",
f"{all_matches[index+1][0]}_G{row_num}"
])
# Upward
for index, match in reversed(list(enumerate(all_matches))):
skill = match[0]
row_num = match[1]
if all_matches[0] != match:
vertical_transitions.append([
"up",
f"{skill}_G{row_num}",
f"{all_matches[index-1][0]}_G{row_num}"
])
return vertical_transitions
def build_all_states(all_transitions):
""" Creates an array with all state labels for the curriculum
Input
- all_transitions: list of lists - all possible up, down, left, or right transitions in curriculum
Output
- all_states: list - a collection of state labels (skill code and grade number)
>>> all_transitions = [['right', 'N1.1.1_G1', 'N1.1.1_G2'], ['right', 'N1.1.1_G2', 'N1.1.1_G3'], ['right', 'N1.1.1_G3', 'N1.1.1_G4'], ['right', 'N1.1.1_G4', 'N1.1.1_G5'], ['right', 'N1.1.1_G5', 'N1.1.1_G6'], ['left', 'N1.1.1_G6', 'N1.1.1_G5'], ['left', 'N1.1.1_G5', 'N1.1.1_G4'], ['left', 'N1.1.1_G4', 'N1.1.1_G3'], ['left', 'N1.1.1_G3', 'N1.1.1_G2'], ['left', 'N1.1.1_G2', 'N1.1.1_G1'], ['right', 'N1.1.2_G1', 'N1.1.2_G2'], ['right', 'N1.1.2_G2', 'N1.1.2_G3'], ['right', 'N1.1.2_G3', 'N1.1.2_G4'], ['right', 'N1.1.2_G4', 'N1.1.2_G5'], ['right', 'N1.1.2_G5', 'N1.1.2_G6'], ['left', 'N1.1.2_G6', 'N1.1.2_G5'], ['left', 'N1.1.2_G5', 'N1.1.2_G4'], ['left', 'N1.1.2_G4', 'N1.1.2_G3'], ['left', 'N1.1.2_G3', 'N1.1.2_G2'], ['left', 'N1.1.2_G2', 'N1.1.2_G1'], ['down', 'N1.1.1_G1', 'N1.1.2_G1'], ['down', 'N1.1.2_G1', 'N1.1.1_G1'], ['down', 'N1.1.1_G2', 'N1.1.2_G2'], ['down', 'N1.1.2_G2', 'N1.1.1_G2'], ['down', 'N1.1.1_G3', 'N1.1.2_G3'], ['down', 'N1.1.2_G3', 'N1.1.1_G3'], ['down', 'N1.1.1_G4', 'N1.1.2_G4'], ['down', 'N1.1.2_G4', 'N1.1.1_G4'], ['down', 'N1.1.1_G5', 'N1.1.2_G5'], ['down', 'N1.1.2_G5', 'N1.1.1_G5'], ['down', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G6', 'N1.1.1_G6'], ['up', 'N1.1.1_G6', 'N1.1.2_G6'], ['up', 'N1.1.2_G5', 'N1.1.1_G5'], ['up', 'N1.1.1_G5', 'N1.1.2_G5'], ['up', 'N1.1.2_G4', 'N1.1.1_G4'], ['up', 'N1.1.1_G4', 'N1.1.2_G4'], ['up', 'N1.1.2_G3', 'N1.1.1_G3'], ['up', 'N1.1.1_G3', 'N1.1.2_G3'], ['up', 'N1.1.2_G2', 'N1.1.1_G2'], ['up', 'N1.1.1_G2', 'N1.1.2_G2'], ['up', 'N1.1.2_G1', 'N1.1.1_G1']]
>>> build_all_states(all_transitions)
['N1.1.1_G1', 'N1.1.1_G2', 'N1.1.1_G3', 'N1.1.1_G4', 'N1.1.1_G5', 'N1.1.1_G6', 'N1.1.2_G1', 'N1.1.2_G2', 'N1.1.2_G3', 'N1.1.2_G4', 'N1.1.2_G5', 'N1.1.2_G6']
"""
all_states = []
for transition in all_transitions:
for index, state in enumerate(transition):
if index == 0:
continue
if state not in all_states:
all_states.append(state)
return all_states
def build_curriculum_logic():
script_df = read_and_preprocess_spreadsheet('Rori_Framework_v1.xlsx')
horizontal_transitions = build_horizontal_transitions(script_df)
vertical_transitions = build_vertical_transitions(script_df)
all_transitions = horizontal_transitions + vertical_transitions
all_states = build_all_states(all_transitions)
return all_states, all_transitions