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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: requisite_processor
3
- Version: 0.2.5
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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "requisite_processor"
3
- version = "0.2.5"
3
+ version = "0.2.7"
4
4
  description = "Module for working with course requisite data and checking requisite satisfaciton."
5
5
  authors = ["Michael Hickman <mhickman@unm.edu>"]
6
6
  readme = "README.md"
@@ -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'] == prereq_data['course']['code'] and (ignore_grades is True or self.__grade_score(course['grade']) >= self.__grade_score(prereq_data['minimum_course_grade'])):
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
- if sub_prereq_data['type'] == 'course' and sub_prereq_data['concurrency_ind'] is True:
161
- continue
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