File size: 9,447 Bytes
d53af6d
 
 
 
 
 
 
a34228d
 
 
d53af6d
a34228d
d53af6d
 
 
 
 
a34228d
 
d53af6d
a34228d
 
d53af6d
a34228d
 
 
 
 
d53af6d
 
 
 
 
 
a34228d
 
 
 
 
 
 
 
 
 
 
 
d53af6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a34228d
 
 
 
 
 
 
 
 
 
 
 
d53af6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a34228d
 
 
 
 
 
 
 
 
 
 
 
d53af6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a34228d
 
 
 
 
 
 
 
 
 
 
 
d53af6d
 
 
 
 
 
 
 
 
 
 
a34228d
d53af6d
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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