requisite_processor 0.2.5__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {requisite_processor-0.2.5 → requisite_processor-0.2.7}/PKG-INFO +3 -2
- {requisite_processor-0.2.5 → requisite_processor-0.2.7}/pyproject.toml +1 -1
- {requisite_processor-0.2.5 → requisite_processor-0.2.7}/requisite_processor/requisite_processor.py +83 -5
- {requisite_processor-0.2.5 → requisite_processor-0.2.7}/README.md +0 -0
- {requisite_processor-0.2.5 → requisite_processor-0.2.7}/requisite_processor/__init__.py +0 -0
- {requisite_processor-0.2.5 → requisite_processor-0.2.7}/requisite_processor/requisite_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: requisite_processor
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Module for working with course requisite data and checking requisite satisfaciton.
|
|
5
5
|
Author: Michael Hickman
|
|
6
6
|
Author-email: mhickman@unm.edu
|
|
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
14
|
Description-Content-Type: text/markdown
|
|
14
15
|
|
|
15
16
|
# Requisite Processor
|
{requisite_processor-0.2.5 → requisite_processor-0.2.7}/requisite_processor/requisite_processor.py
RENAMED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import re
|
|
2
|
+
from typing import List, Union
|
|
2
3
|
from requisite_processor.requisite_check import RequisiteCheckResult
|
|
3
4
|
|
|
5
|
+
def somefunc(a):
|
|
6
|
+
return a
|
|
7
|
+
|
|
4
8
|
class RequisiteProcessor:
|
|
5
|
-
def __init__(self, prerequisites: dict):
|
|
9
|
+
def __init__(self, prerequisites: dict, course_equivalents: Union[dict, None] = None):
|
|
6
10
|
self.prerequisite_rules = prerequisites
|
|
11
|
+
self.course_equivalents = course_equivalents
|
|
7
12
|
|
|
8
13
|
@property
|
|
9
14
|
def availabile_academic_periods(self) -> list[str]:
|
|
@@ -30,7 +35,7 @@ class RequisiteProcessor:
|
|
|
30
35
|
def check_satisfaction(
|
|
31
36
|
self,
|
|
32
37
|
course_code: str,
|
|
33
|
-
courses_taken: list[dict] = None,
|
|
38
|
+
courses_taken: Union[list[dict], None] = None,
|
|
34
39
|
test_scores = None,
|
|
35
40
|
academic_period = None,
|
|
36
41
|
ignore_tests = False,
|
|
@@ -68,6 +73,12 @@ class RequisiteProcessor:
|
|
|
68
73
|
if courses_taken is None:
|
|
69
74
|
courses_taken = []
|
|
70
75
|
|
|
76
|
+
if self.course_equivalents is not None:
|
|
77
|
+
for ct in courses_taken:
|
|
78
|
+
if ct['code'] in self.course_equivalents:
|
|
79
|
+
ct['code'] = self.course_equivalents[ct['code']]['code']
|
|
80
|
+
|
|
81
|
+
|
|
71
82
|
if academic_period is None:
|
|
72
83
|
academic_period = self.latest_available_academic_period
|
|
73
84
|
|
|
@@ -88,6 +99,31 @@ class RequisiteProcessor:
|
|
|
88
99
|
return self.__traverse_prereqs(prereq_rule, academic_period, courses_taken, test_scores, ignore_grades)
|
|
89
100
|
|
|
90
101
|
|
|
102
|
+
def fast_check(self, course_code: str, courses_taken = None, test_scores = None, academic_period = None) -> bool:
|
|
103
|
+
if academic_period is None:
|
|
104
|
+
academic_period = self.latest_available_academic_period
|
|
105
|
+
|
|
106
|
+
if academic_period not in self.prerequisite_rules:
|
|
107
|
+
raise self.AcademicPeriodNotFound(f"The academic period '{academic_period}' is not found in the given prerequisite data.")
|
|
108
|
+
|
|
109
|
+
if course_code not in self.prerequisite_rules[academic_period]:
|
|
110
|
+
raise self.CourseNotFound(f"Course {course_code} not found in {academic_period}")
|
|
111
|
+
|
|
112
|
+
prereq_rule = self.prerequisite_rules[academic_period][course_code]
|
|
113
|
+
|
|
114
|
+
if prereq_rule is None:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
if courses_taken is None:
|
|
118
|
+
courses_taken = []
|
|
119
|
+
|
|
120
|
+
if test_scores is None:
|
|
121
|
+
test_scores = {}
|
|
122
|
+
|
|
123
|
+
return self.__fast_check(prereq_rule, courses_taken, test_scores)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
91
127
|
def __grade_score(self, grade) -> float:
|
|
92
128
|
grades = {
|
|
93
129
|
'A+': 4.3, 'A': 4, 'A-': 3.7,
|
|
@@ -104,9 +140,13 @@ class RequisiteProcessor:
|
|
|
104
140
|
|
|
105
141
|
def __traverse_prereqs(self, prereq_data: dict, academic_period, courses_taken, test_scores, ignore_grades):
|
|
106
142
|
if prereq_data['type'] == 'course':
|
|
143
|
+
prereq_code = prereq_data['course']['code']
|
|
144
|
+
if self.course_equivalents is not None and prereq_code in self.course_equivalents:
|
|
145
|
+
prereq_code = self.course_equivalents[prereq_code]['code']
|
|
146
|
+
|
|
107
147
|
selected_course = None
|
|
108
148
|
for course in courses_taken:
|
|
109
|
-
if course['code'] ==
|
|
149
|
+
if course['code'] == prereq_code and (ignore_grades is True or self.__grade_score(course['grade']) >= self.__grade_score(prereq_data['minimum_course_grade'])):
|
|
110
150
|
selected_course = course
|
|
111
151
|
break
|
|
112
152
|
|
|
@@ -157,8 +197,9 @@ class RequisiteProcessor:
|
|
|
157
197
|
prereq_operands = []
|
|
158
198
|
|
|
159
199
|
for sub_prereq_data in prereq_data['operands']:
|
|
160
|
-
|
|
161
|
-
|
|
200
|
+
# TODO : Consider how to handle concurrency
|
|
201
|
+
# if sub_prereq_data['type'] == 'course' and sub_prereq_data['concurrency_ind'] is True:
|
|
202
|
+
# continue
|
|
162
203
|
|
|
163
204
|
prereq_operands.append(sub_prereq_data)
|
|
164
205
|
|
|
@@ -187,6 +228,43 @@ class RequisiteProcessor:
|
|
|
187
228
|
return result
|
|
188
229
|
|
|
189
230
|
|
|
231
|
+
def __fast_check(self, prereq_data: dict, courses_taken: List[str], test_scores: dict) -> bool:
|
|
232
|
+
if prereq_data['type'] == 'placement_test':
|
|
233
|
+
test_code = prereq_data['placement_test']['test_code']
|
|
234
|
+
if test_code not in test_scores:
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
if test_scores[test_code] >= prereq_data['minimum_test_score']:
|
|
238
|
+
return True
|
|
239
|
+
else:
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
if prereq_data['type'] == 'course':
|
|
243
|
+
for course in courses_taken:
|
|
244
|
+
if course == prereq_data['course']['code']:
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
if prereq_data['type'] == 'and':
|
|
251
|
+
for sub_prereq_data in prereq_data['operands']:
|
|
252
|
+
if self.__fast_check(sub_prereq_data, courses_taken, test_scores) is False:
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
return True
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if prereq_data['type'] == 'or':
|
|
259
|
+
for sub_prereq_data in prereq_data['operands']:
|
|
260
|
+
if self.__fast_check(sub_prereq_data, courses_taken, test_scores) is True:
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
|
|
190
268
|
def __reduce_rule(self, prereq_data, filter_rule):
|
|
191
269
|
if prereq_data is None:
|
|
192
270
|
return None
|
|
File without changes
|
|
File without changes
|
{requisite_processor-0.2.5 → requisite_processor-0.2.7}/requisite_processor/requisite_check.py
RENAMED
|
File without changes
|