still automating degree checking
This commit is contained in:
parent
164541d826
commit
403cfc533b
296
degrees.py
296
degrees.py
|
|
@ -1,6 +1,15 @@
|
||||||
from lark import Lark, Transformer, v_args
|
from lark import Lark, Transformer, v_args
|
||||||
import json, sys, re, codecs
|
import json, sys, re, codecs
|
||||||
import lark
|
|
||||||
|
|
||||||
|
debug_out = codecs.open("cache/degrees_debug.txt", "w", "utf-8")
|
||||||
|
|
||||||
|
def d(s):
|
||||||
|
#if type(s) == tuple or type(s) == list:
|
||||||
|
# debug_out.write(" ".join(str(s)) + "\n")
|
||||||
|
debug_out.write(str(s) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""describe college courses, their number of units, any other courses that are prerequisites, as well as various degrees which
|
"""describe college courses, their number of units, any other courses that are prerequisites, as well as various degrees which
|
||||||
|
|
@ -10,32 +19,6 @@ consist of groups of courses that must be taken. The groups have rules associate
|
||||||
- take at least n units from the following list (so the taken course's unit value must add up to n or greater)
|
- take at least n units from the following list (so the taken course's unit value must add up to n or greater)
|
||||||
- and so on."""
|
- and so on."""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def num(s):
|
|
||||||
if s == '': return 0
|
|
||||||
try:
|
|
||||||
return int(s)
|
|
||||||
except ValueError:
|
|
||||||
return float(s)
|
|
||||||
|
|
||||||
def num_units(s):
|
|
||||||
if s == '': return 0
|
|
||||||
m = re.match(r'^(\d+)\.?(\d+)?\s?(units)?$',s)
|
|
||||||
if m:
|
|
||||||
if m.group(1) and m.group(2):
|
|
||||||
if m.group(2) == '0':
|
|
||||||
return int(m.group(1))
|
|
||||||
return float(f"{m.group(1)}.{m.group(2)}")
|
|
||||||
elif m.group(1):
|
|
||||||
return int(m.group(1))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
return int(s)
|
|
||||||
except ValueError:
|
|
||||||
return float(s)
|
|
||||||
|
|
||||||
|
|
||||||
class CourseOr:
|
class CourseOr:
|
||||||
def __init__(self, subs):
|
def __init__(self, subs):
|
||||||
self.names = subs
|
self.names = subs
|
||||||
|
|
@ -45,68 +28,46 @@ class CourseOr:
|
||||||
|
|
||||||
|
|
||||||
class Course:
|
class Course:
|
||||||
def __init__(self, name, units, prerequisites=[]):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.units = units
|
|
||||||
self.prerequisites = prerequisites
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
#return f"{self.name} ({self.units} units)"
|
|
||||||
return f"{self.name}"
|
return f"{self.name}"
|
||||||
|
|
||||||
class CourseList:
|
class CourseList:
|
||||||
def __init__(self, name, course_list):
|
def __init__(self, name, courses):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.course_list = course_list
|
self.courses = courses
|
||||||
self.l = len(course_list)
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.course_list)
|
return iter(self.courses)
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
print("What is this len? ", len(self.course_list), str(list(self.course_list)), str(type(self.course_list)))
|
return len(self.courses)
|
||||||
i = 1
|
|
||||||
for c in self.course_list:
|
|
||||||
i += 1
|
|
||||||
return i
|
|
||||||
#return len(self.course_list)
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ", ".join([f"\"{str(x)}\"" for x in self.course_list]) # .course_list
|
return ", ".join([f"\"{str(x)}\"" for x in self.courses])
|
||||||
#return f"{s}"
|
|
||||||
#return f"CourseList({self.name}, {s})"
|
|
||||||
|
|
||||||
class DegreeRule:
|
class DegreeRule:
|
||||||
|
rule_count = 1
|
||||||
def __init__(self, rule_type, course_list):
|
def __init__(self, rule_type, course_list):
|
||||||
self.rule_type = rule_type
|
self.rule_type = rule_type
|
||||||
if 'n' in rule_type.__dict__: self.n = rule_type.n
|
if 'n' in rule_type.__dict__: self.n = rule_type.n
|
||||||
self.course_list = course_list
|
self.course_list = course_list
|
||||||
print("COURSELIST: ", type(self.course_list), self.course_list)
|
self.rule_number = DegreeRule.rule_count
|
||||||
def mz_rule(self,n):
|
DegreeRule.rule_count += 1
|
||||||
s = ",".join( [ f"{x}" for x in self.course_list])
|
|
||||||
i = 0
|
|
||||||
for c in self.course_list:
|
|
||||||
i += 1
|
|
||||||
#print("What is this len? ", len(self.course_list), str(list(self.course_list)), str(type(self.course_list)))
|
|
||||||
t = len(list(self.course_list))
|
|
||||||
if not type(self.course_list)==tuple:
|
|
||||||
t = self.course_list.l
|
|
||||||
return f"array[1..{t}] of string: rule_{n}_courses = [{s}];"
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.mz_rule(1)
|
s = ",".join( [ f"{x}" for x in self.course_list])
|
||||||
#return f"DegreeRule({self.rule_type}, {self.course_list})"
|
the_rule = f"% {self.rule_type}\n"
|
||||||
|
return the_rule + f"array[1..{len(self.course_list)}] of string: rule_{self.rule_number}_courses = [{s}];"
|
||||||
|
|
||||||
class Degree:
|
class Degree:
|
||||||
def __init__(self, name, degree_rules):
|
def __init__(self, name, degree_rules):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.degree_rules = degree_rules
|
self.degree_rules = degree_rules
|
||||||
def mz_rule(self):
|
def __repr__(self):
|
||||||
n = 1
|
n = 1
|
||||||
s = f"% Degree: {self.name}\n"
|
s = f"% Degree: {self.name}\n"
|
||||||
for r in self.degree_rules:
|
for r in self.degree_rules:
|
||||||
s += r.mz_rule(n) + "\n"
|
s += str(r) + "\n"
|
||||||
n += 1
|
n += 1
|
||||||
return s
|
return s
|
||||||
def __repr__(self):
|
|
||||||
#x = ", ".join([str(x) for x in self.degree_rules])
|
|
||||||
#return f"Degree({self.name}, {x})"
|
|
||||||
return self.mz_rule()
|
|
||||||
|
|
||||||
class RuleType():
|
class RuleType():
|
||||||
def __init__(self,what,n=0):
|
def __init__(self,what,n=0):
|
||||||
|
|
@ -143,94 +104,70 @@ class DSLTransformer(Transformer):
|
||||||
self.lists = {}
|
self.lists = {}
|
||||||
|
|
||||||
def course_declaration(self, name, units):
|
def course_declaration(self, name, units):
|
||||||
#return Course(name, int(units), prerequisites.children)
|
d("\ncourse_declaration")
|
||||||
#print(items)
|
d(name)
|
||||||
#if type(items[0]) == str:
|
d(units)
|
||||||
c = Course(name.value, units)
|
|
||||||
self.courses[name.value] = c
|
|
||||||
#else:
|
|
||||||
#options = [x.value for x in items[0].children]
|
|
||||||
#label = "[OR " + options[0] + "...]"
|
|
||||||
#c = CourseOr(label, options)
|
|
||||||
#self.courses[label] = c
|
|
||||||
|
|
||||||
|
|
||||||
'''def program(self, name, rules):
|
|
||||||
self.degrees[name.value] = Degree(name.value, rules)
|
|
||||||
return self.degrees[name.value]'''
|
|
||||||
|
|
||||||
def rule_type(self, items):
|
|
||||||
what = items[0]
|
|
||||||
n = None
|
|
||||||
if len(items) == 2:
|
|
||||||
n = int(items[1])
|
|
||||||
return RuleType(what, n)
|
|
||||||
|
|
||||||
def take_all(self):
|
def take_all(self):
|
||||||
|
d("\ntake_all")
|
||||||
return TakeAll()
|
return TakeAll()
|
||||||
|
|
||||||
def take_n_courses(self, n):
|
def take_n_courses(self, n):
|
||||||
|
d("\ntake_n_courses")
|
||||||
|
d(n)
|
||||||
return TakeN(n)
|
return TakeN(n)
|
||||||
|
|
||||||
def take_n_units(self, n):
|
def take_n_units(self, n):
|
||||||
|
d("\ntake_n_units")
|
||||||
|
d(n)
|
||||||
return TakeNUnits(n)
|
return TakeNUnits(n)
|
||||||
|
|
||||||
def degree_rule(self, rule_type, *course_list):
|
def degree_rule(self, rule_type, course_list):
|
||||||
#if type(course_list[0])==type([]):
|
d("\ndegree_rule")
|
||||||
# return DegreeRule(rule_type, course_list[0])
|
d(rule_type)
|
||||||
print("making DegreeRule: ", rule_type, type(course_list), course_list)
|
d(course_list)
|
||||||
return DegreeRule(rule_type, course_list)
|
return DegreeRule(rule_type, course_list)
|
||||||
|
|
||||||
def list_declaration(self, name, course_list):
|
|
||||||
print("declared course list: ", name.value, course_list)
|
|
||||||
cl = CourseList(name.value, course_list)
|
|
||||||
self.lists[name.value] = cl
|
|
||||||
return cl
|
|
||||||
|
|
||||||
def list_ref(self, name):
|
def list_ref(self, name):
|
||||||
print("referenced course list: ", name.value, type(self.lists[name.value]), self.lists[name.value])
|
d("\nlist_ref")
|
||||||
return self.lists[name.value]
|
d(name)
|
||||||
|
return self.lists[name]
|
||||||
|
|
||||||
def course(self, *items):
|
def course(self, c):
|
||||||
return Course(items[0].value,"")
|
d("\ncourse")
|
||||||
|
d(c)
|
||||||
|
return Course(c.value)
|
||||||
|
|
||||||
def course_or(self, *items):
|
def course_or(self, *items):
|
||||||
return CourseOr([x.value for x in items])
|
d("\ncourse_or")
|
||||||
|
d(items)
|
||||||
|
return CourseOr(items)
|
||||||
|
|
||||||
def course_list(self, *items):
|
def course_list(self, *items):
|
||||||
#print(str(items))
|
if items[-1] == None: items = items[:-1]
|
||||||
print("course list: ", items)
|
d("\ncourse_list")
|
||||||
return CourseList("temp", items)
|
d(items)
|
||||||
#return [c for c in items]
|
return items
|
||||||
|
|
||||||
def program(self, *items):
|
def list_declaration(self, name, course_list):
|
||||||
#print(items)
|
d("\nlist_declaration")
|
||||||
name = items[0].value
|
d(name)
|
||||||
rules = items[1:]
|
d(course_list)
|
||||||
self.degrees[name] = Degree(name, rules)
|
c = CourseList(name, course_list)
|
||||||
return self.degrees[name]
|
self.lists[name] = c
|
||||||
|
return c
|
||||||
"""
|
|
||||||
dsl = DSLTransformer()
|
def program(self, name, *rules):
|
||||||
dsl.course('CS101', 3)
|
d("\nprogram")
|
||||||
dsl.course('CS102', 3, prerequisites=['CS101'])
|
d(name)
|
||||||
dsl.course('MATH101', 3)
|
d(rules)
|
||||||
dsl.degree('CS', [
|
dg = Degree(name, rules)
|
||||||
dsl.take_all(['CS101', 'CS102']),
|
self.degrees[name] = dg
|
||||||
dsl.take_n_units(['CS101', 'CS102', 'MATH101'], 9),
|
return dg
|
||||||
dsl.take_n(['CS101', 'CS102', 'MATH101'], 2),
|
|
||||||
])
|
|
||||||
|
|
||||||
print(dsl.courses)
|
|
||||||
print(dsl.degrees)
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#(course ","?)*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
grammar = """
|
grammar = """
|
||||||
start: _spec+
|
start: _spec+
|
||||||
|
|
@ -273,25 +210,9 @@ grammar = """
|
||||||
|
|
||||||
def parser():
|
def parser():
|
||||||
dsl = """
|
dsl = """
|
||||||
course CSIS1 2 units
|
|
||||||
course CSIS2 4 units
|
|
||||||
course ACCT120 3 units
|
|
||||||
course ACCT20 4 units
|
|
||||||
course BUS1 3 units
|
|
||||||
course ENGL250 3 units
|
|
||||||
course ACCT105 3 units
|
|
||||||
course ACCT121 3 units
|
|
||||||
course BOT100 3 units
|
|
||||||
course BOT105B 3 units
|
|
||||||
|
|
||||||
list a1: CMUN1A, CMUN5, CMUN8, CMUN10
|
list a1: CMUN1A, CMUN5, CMUN8, CMUN10
|
||||||
list a2: ENGL1A
|
list a2: ENGL1A
|
||||||
list a3: PHIL2, PHIL4, ENGL1C, CMUN3
|
list a3: PHIL2, PHIL4, ENGL1C, CMUN3
|
||||||
list b1: ASTR1, CHEM1A, CHEM1B, CHEM12A, CHEM12B
|
|
||||||
list b2: AH15, ANTH1, BIO1, BIO2, BIO4, BIO5
|
|
||||||
list b3: CHEM1A, ANTH1, BIO4, BIO5
|
|
||||||
list b4: BUS14, BUS11 or ECON11, ECON14, MATH1A, MATH1B
|
|
||||||
|
|
||||||
list c1: BUS1, ENGL250, ACCT120, BOT100, ACCT121, BOT105B
|
list c1: BUS1, ENGL250, ACCT120, BOT100, ACCT121, BOT105B
|
||||||
|
|
||||||
program BusinessAccountingOption
|
program BusinessAccountingOption
|
||||||
|
|
@ -312,11 +233,11 @@ def parser():
|
||||||
|
|
||||||
def do_parse(dsl):
|
def do_parse(dsl):
|
||||||
parser = Lark(grammar)
|
parser = Lark(grammar)
|
||||||
print(parser.parse(dsl).pretty())
|
#d(parser.parse(dsl).pretty())
|
||||||
|
|
||||||
print("\n\n\n")
|
#d("\n\n\n")
|
||||||
|
|
||||||
print("\nTRANSFORMER: ")
|
#d("\nTRANSFORMER: ")
|
||||||
|
|
||||||
parser = Lark(grammar)
|
parser = Lark(grammar)
|
||||||
transformer = DSLTransformer()
|
transformer = DSLTransformer()
|
||||||
|
|
@ -337,53 +258,11 @@ def do_parse(dsl):
|
||||||
[print(c) for c in transformer.courses]
|
[print(c) for c in transformer.courses]
|
||||||
print()
|
print()
|
||||||
print("\nDEGREES: ")
|
print("\nDEGREES: ")
|
||||||
for d,i in transformer.degrees.items():
|
for deg,i in transformer.degrees.items():
|
||||||
print(str(i))
|
print(str(i))
|
||||||
|
|
||||||
print()
|
print()
|
||||||
[print(d) for d in transformer.lists]
|
[print(deg) for deg in transformer.lists]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def rule_check_units():
|
|
||||||
# Define the course units and passing grades
|
|
||||||
course_units = {
|
|
||||||
"A": 1,
|
|
||||||
"B": 2,
|
|
||||||
"C": 1,
|
|
||||||
"D": 3
|
|
||||||
}
|
|
||||||
|
|
||||||
# Define the students and their passed courses
|
|
||||||
students = {
|
|
||||||
"Peter": ["A", "B", "C"],
|
|
||||||
"Mark": ["D", "B"],
|
|
||||||
"Susan": ["A", "B"]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Define the minimum units required to graduate
|
|
||||||
min_units_to_graduate = 4
|
|
||||||
|
|
||||||
calcs = {}
|
|
||||||
for student, passed_courses in students.items():
|
|
||||||
print("\n", student, passed_courses)
|
|
||||||
for c in passed_courses:
|
|
||||||
print(f"\t{c:15} {course_units[c]}")
|
|
||||||
print(f"\t{'Total':15} {sum(course_units[c] for c in passed_courses)}")
|
|
||||||
|
|
||||||
# Determine the graduating students
|
|
||||||
graduating_students = [
|
|
||||||
student for student, passed_courses in students.items()
|
|
||||||
if sum(course_units[course] for course in passed_courses) >= min_units_to_graduate
|
|
||||||
]
|
|
||||||
|
|
||||||
# Print the graduating students
|
|
||||||
if graduating_students:
|
|
||||||
print("The graduating students are:", ", ".join(graduating_students))
|
|
||||||
else:
|
|
||||||
print("No students meet the graduation requirements.")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -391,27 +270,6 @@ def rule_check_units():
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def compute_degree_progress(student_courses, degree):
|
|
||||||
total_units_completed = 0
|
|
||||||
total_required_units = 0
|
|
||||||
for course in degree['required_courses']:
|
|
||||||
if course['id'] in student_courses:
|
|
||||||
total_units_completed += course['units']
|
|
||||||
if 'additional_rules' in course:
|
|
||||||
for rule in course['additional_rules']:
|
|
||||||
if rule['type'] == 'at_least_n_units_from_list':
|
|
||||||
units_from_list = sum([c['units'] for c in degree['courses'] if c['id'] in student_courses and c['id'] in rule['course_list']])
|
|
||||||
if units_from_list < rule['n']:
|
|
||||||
break
|
|
||||||
total_required_units += course['units']
|
|
||||||
progress_percent = total_units_completed / total_required_units * 100 if total_required_units > 0 else 0
|
|
||||||
return progress_percent
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
word_to_num = {'course':1, 'One':1, 'one':1, 'two':2, 'three':3, 'four':4, 'five':5, 'six':6,
|
word_to_num = {'course':1, 'One':1, 'one':1, 'two':2, 'three':3, 'four':4, 'five':5, 'six':6,
|
||||||
'seven':7, 'eight':8, 'nine':9, 'ten':10, 'eleven':11,}
|
'seven':7, 'eight':8, 'nine':9, 'ten':10, 'eleven':11,}
|
||||||
|
|
||||||
|
|
@ -510,7 +368,7 @@ def check_ands_ors_pbd(award, pbd, verbose=0):
|
||||||
if verbose: print()
|
if verbose: print()
|
||||||
|
|
||||||
|
|
||||||
def build_catalog():
|
def build_program_rules():
|
||||||
cfile = "cache/courses/courses_active_built.json"
|
cfile = "cache/courses/courses_active_built.json"
|
||||||
pfile = "cache/programs/programs_built.json"
|
pfile = "cache/programs/programs_built.json"
|
||||||
|
|
||||||
|
|
@ -630,7 +488,7 @@ def build_catalog():
|
||||||
if ok:
|
if ok:
|
||||||
okay.append(p)
|
okay.append(p)
|
||||||
|
|
||||||
print("These programs are okay:")
|
print("\n\n\n\nThese programs are okay:")
|
||||||
for p in okay:
|
for p in okay:
|
||||||
for l in p:
|
for l in p:
|
||||||
print(l)
|
print(l)
|
||||||
|
|
@ -646,20 +504,16 @@ def build_catalog():
|
||||||
print(f"not okay: {len(notokay)}")
|
print(f"not okay: {len(notokay)}")
|
||||||
return
|
return
|
||||||
do_parse('\n'.join(course_spec))
|
do_parse('\n'.join(course_spec))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## TODO or and and
|
|
||||||
## How to see this as a 'fake' course: Eligible for English 1A ?
|
|
||||||
|
|
||||||
## How to include this as part of a list: 'ANY COURSE NOT USED IN LIST A (3 units)'
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
options = { 1: ['parsing example',parser] ,
|
options = { 1: ['parsing example',parser] ,
|
||||||
2: ['rule check by units',rule_check_units] ,
|
2: ['build program rules', build_program_rules],
|
||||||
3: ['load all programs',build_catalog] ,
|
|
||||||
}
|
}
|
||||||
print ('')
|
print ('')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,680 @@
|
||||||
|
from lark import Lark, Transformer, v_args
|
||||||
|
import json, sys, re, codecs
|
||||||
|
import lark
|
||||||
|
|
||||||
|
|
||||||
|
"""describe college courses, their number of units, any other courses that are prerequisites, as well as various degrees which
|
||||||
|
consist of groups of courses that must be taken. The groups have rules associated with them, such as:
|
||||||
|
- take all (the following courses)
|
||||||
|
- take n (where n=3 or 5 or something)
|
||||||
|
- take at least n units from the following list (so the taken course's unit value must add up to n or greater)
|
||||||
|
- and so on."""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def num(s):
|
||||||
|
if s == '': return 0
|
||||||
|
try:
|
||||||
|
return int(s)
|
||||||
|
except ValueError:
|
||||||
|
return float(s)
|
||||||
|
|
||||||
|
def num_units(s):
|
||||||
|
if s == '': return 0
|
||||||
|
m = re.match(r'^(\d+)\.?(\d+)?\s?(units)?$',s)
|
||||||
|
if m:
|
||||||
|
if m.group(1) and m.group(2):
|
||||||
|
if m.group(2) == '0':
|
||||||
|
return int(m.group(1))
|
||||||
|
return float(f"{m.group(1)}.{m.group(2)}")
|
||||||
|
elif m.group(1):
|
||||||
|
return int(m.group(1))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return int(s)
|
||||||
|
except ValueError:
|
||||||
|
return float(s)
|
||||||
|
|
||||||
|
|
||||||
|
class CourseOr:
|
||||||
|
def __init__(self, subs):
|
||||||
|
self.names = subs
|
||||||
|
self.name = " or ".join(self.names)
|
||||||
|
def __repr__(self):
|
||||||
|
return f"[{self.name}]"
|
||||||
|
|
||||||
|
|
||||||
|
class Course:
|
||||||
|
def __init__(self, name, units, prerequisites=[]):
|
||||||
|
self.name = name
|
||||||
|
self.units = units
|
||||||
|
self.prerequisites = prerequisites
|
||||||
|
def __repr__(self):
|
||||||
|
#return f"{self.name} ({self.units} units)"
|
||||||
|
return f"{self.name}"
|
||||||
|
|
||||||
|
class CourseList:
|
||||||
|
def __init__(self, name, course_list):
|
||||||
|
self.name = name
|
||||||
|
self.course_list = course_list
|
||||||
|
self.l = len(course_list)
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.course_list)
|
||||||
|
def __len__(self):
|
||||||
|
print("What is this len? ", len(self.course_list), str(list(self.course_list)), str(type(self.course_list)))
|
||||||
|
i = 1
|
||||||
|
for c in self.course_list:
|
||||||
|
i += 1
|
||||||
|
return i
|
||||||
|
#return len(self.course_list)
|
||||||
|
def __repr__(self):
|
||||||
|
return ", ".join([f"\"{str(x)}\"" for x in self.course_list]) # .course_list
|
||||||
|
#return f"{s}"
|
||||||
|
#return f"CourseList({self.name}, {s})"
|
||||||
|
|
||||||
|
class DegreeRule:
|
||||||
|
def __init__(self, rule_type, course_list):
|
||||||
|
self.rule_type = rule_type
|
||||||
|
if 'n' in rule_type.__dict__: self.n = rule_type.n
|
||||||
|
self.course_list = course_list
|
||||||
|
print("COURSELIST: ", type(self.course_list), self.course_list)
|
||||||
|
def mz_rule(self,n):
|
||||||
|
s = ",".join( [ f"{x}" for x in self.course_list])
|
||||||
|
i = 0
|
||||||
|
for c in self.course_list:
|
||||||
|
i += 1
|
||||||
|
#print("What is this len? ", len(self.course_list), str(list(self.course_list)), str(type(self.course_list)))
|
||||||
|
t = len(list(self.course_list))
|
||||||
|
if not type(self.course_list)==tuple:
|
||||||
|
t = self.course_list.l
|
||||||
|
return f"array[1..{t}] of string: rule_{n}_courses = [{s}];"
|
||||||
|
def __repr__(self):
|
||||||
|
return self.mz_rule(1)
|
||||||
|
#return f"DegreeRule({self.rule_type}, {self.course_list})"
|
||||||
|
|
||||||
|
class Degree:
|
||||||
|
def __init__(self, name, degree_rules):
|
||||||
|
self.name = name
|
||||||
|
self.degree_rules = degree_rules
|
||||||
|
def mz_rule(self):
|
||||||
|
n = 1
|
||||||
|
s = f"% Degree: {self.name}\n"
|
||||||
|
for r in self.degree_rules:
|
||||||
|
s += r.mz_rule(n)
|
||||||
|
n += 1
|
||||||
|
return s
|
||||||
|
def __repr__(self):
|
||||||
|
#x = ", ".join([str(x) for x in self.degree_rules])
|
||||||
|
#return f"Degree({self.name}, {x})"
|
||||||
|
return self.mz_rule()
|
||||||
|
|
||||||
|
class RuleType():
|
||||||
|
def __init__(self,what,n=0):
|
||||||
|
self.what = what
|
||||||
|
self.n = n
|
||||||
|
def __repr__(self):
|
||||||
|
return "" #f"RuleType({self.what}, {self.n})"
|
||||||
|
|
||||||
|
class Rule:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TakeAll(Rule):
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Take all:"
|
||||||
|
|
||||||
|
class TakeN(Rule):
|
||||||
|
def __init__(self, n):
|
||||||
|
self.n = n
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Take {self.n} courses:"
|
||||||
|
|
||||||
|
class TakeNUnits(Rule):
|
||||||
|
def __init__(self, n):
|
||||||
|
self.n = n
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Take {self.n} units:"
|
||||||
|
|
||||||
|
|
||||||
|
@v_args(inline=True)
|
||||||
|
class DSLTransformer(Transformer):
|
||||||
|
def __init__(self):
|
||||||
|
self.courses = {}
|
||||||
|
self.degrees = {}
|
||||||
|
self.lists = {}
|
||||||
|
|
||||||
|
def course_declaration(self, name, units):
|
||||||
|
#return Course(name, int(units), prerequisites.children)
|
||||||
|
#print(items)
|
||||||
|
#if type(items[0]) == str:
|
||||||
|
c = Course(name.value, units)
|
||||||
|
self.courses[name.value] = c
|
||||||
|
#else:
|
||||||
|
#options = [x.value for x in items[0].children]
|
||||||
|
#label = "[OR " + options[0] + "...]"
|
||||||
|
#c = CourseOr(label, options)
|
||||||
|
#self.courses[label] = c
|
||||||
|
|
||||||
|
|
||||||
|
'''def program(self, name, rules):
|
||||||
|
self.degrees[name.value] = Degree(name.value, rules)
|
||||||
|
return self.degrees[name.value]'''
|
||||||
|
|
||||||
|
def rule_type(self, items):
|
||||||
|
what = items[0]
|
||||||
|
n = None
|
||||||
|
if len(items) == 2:
|
||||||
|
n = int(items[1])
|
||||||
|
return RuleType(what, n)
|
||||||
|
|
||||||
|
def take_all(self):
|
||||||
|
return TakeAll()
|
||||||
|
|
||||||
|
def take_n_courses(self, n):
|
||||||
|
return TakeN(n)
|
||||||
|
|
||||||
|
def take_n_units(self, n):
|
||||||
|
return TakeNUnits(n)
|
||||||
|
|
||||||
|
def degree_rule(self, rule_type, *course_list):
|
||||||
|
#if type(course_list[0])==type([]):
|
||||||
|
# return DegreeRule(rule_type, course_list[0])
|
||||||
|
print("making DegreeRule: ", rule_type, type(course_list), course_list)
|
||||||
|
return DegreeRule(rule_type, course_list)
|
||||||
|
|
||||||
|
def list_declaration(self, name, course_list):
|
||||||
|
print("declared course list: ", name.value, course_list)
|
||||||
|
cl = CourseList(name.value, course_list)
|
||||||
|
self.lists[name.value] = cl
|
||||||
|
return cl
|
||||||
|
|
||||||
|
def list_ref(self, name):
|
||||||
|
print("referenced course list: ", name.value, type(self.lists[name.value]), self.lists[name.value])
|
||||||
|
return self.lists[name.value]
|
||||||
|
|
||||||
|
def course(self, *items):
|
||||||
|
return Course(items[0].value,"")
|
||||||
|
|
||||||
|
def course_or(self, *items):
|
||||||
|
return CourseOr([x.value for x in items])
|
||||||
|
|
||||||
|
def course_list(self, *items):
|
||||||
|
#print(str(items))
|
||||||
|
print("course list: ", items)
|
||||||
|
return CourseList("temp", items)
|
||||||
|
#return [c for c in items]
|
||||||
|
|
||||||
|
def program(self, *items):
|
||||||
|
#print(items)
|
||||||
|
name = items[0].value
|
||||||
|
rules = items[1:]
|
||||||
|
self.degrees[name] = Degree(name, rules)
|
||||||
|
return self.degrees[name]
|
||||||
|
|
||||||
|
"""
|
||||||
|
dsl = DSLTransformer()
|
||||||
|
dsl.course('CS101', 3)
|
||||||
|
dsl.course('CS102', 3, prerequisites=['CS101'])
|
||||||
|
dsl.course('MATH101', 3)
|
||||||
|
dsl.degree('CS', [
|
||||||
|
dsl.take_all(['CS101', 'CS102']),
|
||||||
|
dsl.take_n_units(['CS101', 'CS102', 'MATH101'], 9),
|
||||||
|
dsl.take_n(['CS101', 'CS102', 'MATH101'], 2),
|
||||||
|
])
|
||||||
|
|
||||||
|
print(dsl.courses)
|
||||||
|
print(dsl.degrees)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#(course ","?)*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
grammar = """
|
||||||
|
start: _spec+
|
||||||
|
|
||||||
|
_spec: program | course_declaration | list_declaration
|
||||||
|
|
||||||
|
program: "program" PROGRAMNAME degree_rule*
|
||||||
|
|
||||||
|
degree_rule: "take" rule_type [course_list | list_ref]
|
||||||
|
|
||||||
|
list_ref: LISTNAME
|
||||||
|
|
||||||
|
rule_type: "all from" -> take_all
|
||||||
|
| "at least" INT "units from" -> take_n_units
|
||||||
|
| "at least" INT "courses from" -> take_n_courses
|
||||||
|
| "at least" INT "course from" -> take_n_courses
|
||||||
|
|
||||||
|
list_declaration: "list" LISTNAME ":" course_list
|
||||||
|
|
||||||
|
|
||||||
|
course_declaration: "course" COURSECODE UNITAMOUNT "units" ["prerequisites" (COURSECODE ","?)*]
|
||||||
|
|
||||||
|
course_list: [course | course_or] ["," [course | course_or]]*
|
||||||
|
|
||||||
|
course: COURSECODE
|
||||||
|
|
||||||
|
course_or: COURSECODE "or" COURSECODE
|
||||||
|
|
||||||
|
COURSECODE: ("A".."Z")+ INT+ ["A".."Z"]*
|
||||||
|
PROGRAMNAME: ("A".."Z" | "a".."z")+
|
||||||
|
LISTNAME: ("a".."z"["A".."Z" | "a".."z" | INT]+)
|
||||||
|
|
||||||
|
UNITAMOUNT: NUMUNITS | NUMUNITS "-" NUMUNITS
|
||||||
|
NUMUNITS: INT | INT "." INT
|
||||||
|
%import common.INT
|
||||||
|
%import common.WS
|
||||||
|
%ignore WS
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def parser():
|
||||||
|
dsl = """
|
||||||
|
course CSIS1 2 units
|
||||||
|
course CSIS2 4 units
|
||||||
|
course ACCT120 3 units
|
||||||
|
course ACCT20 4 units
|
||||||
|
course BUS1 3 units
|
||||||
|
course ENGL250 3 units
|
||||||
|
course ACCT105 3 units
|
||||||
|
course ACCT121 3 units
|
||||||
|
course BOT100 3 units
|
||||||
|
course BOT105B 3 units
|
||||||
|
|
||||||
|
list a1: CMUN1A, CMUN5, CMUN8, CMUN10
|
||||||
|
list a2: ENGL1A
|
||||||
|
list a3: PHIL2, PHIL4, ENGL1C, CMUN3
|
||||||
|
list b1: ASTR1, CHEM1A, CHEM1B, CHEM12A, CHEM12B
|
||||||
|
list b2: AH15, ANTH1, BIO1, BIO2, BIO4, BIO5
|
||||||
|
list b3: CHEM1A, ANTH1, BIO4, BIO5
|
||||||
|
list b4: BUS14, BUS11 or ECON11, ECON14, MATH1A, MATH1B
|
||||||
|
|
||||||
|
list c1: BUS1, ENGL250, ACCT120, BOT100, ACCT121, BOT105B
|
||||||
|
|
||||||
|
program BusinessAccountingOption
|
||||||
|
take all from CSIS1, CSIS2
|
||||||
|
take at least 6 units from ACCT120, ACCT20 or ECON20
|
||||||
|
take at least 3 courses from MUS5, MUS10, BUS1, ENGL250, ACCT120, BOT100, ACCT121, BOT105B
|
||||||
|
take at least 1 course from c1
|
||||||
|
program CSUGenEd
|
||||||
|
take at least 1 course from a1
|
||||||
|
take at least 1 course from a2
|
||||||
|
take at least 1 course from a3
|
||||||
|
"""
|
||||||
|
do_parse(dsl)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_parse(dsl):
|
||||||
|
parser = Lark(grammar)
|
||||||
|
print(parser.parse(dsl).pretty())
|
||||||
|
|
||||||
|
print("\n\n\n")
|
||||||
|
|
||||||
|
print("\nTRANSFORMER: ")
|
||||||
|
|
||||||
|
parser = Lark(grammar)
|
||||||
|
transformer = DSLTransformer()
|
||||||
|
|
||||||
|
def parse_dsl(dsl):
|
||||||
|
tree = parser.parse(dsl)
|
||||||
|
return transformer.transform(tree)
|
||||||
|
|
||||||
|
|
||||||
|
result = parse_dsl(dsl)
|
||||||
|
|
||||||
|
print(transformer.courses)
|
||||||
|
print()
|
||||||
|
print(transformer.degrees)
|
||||||
|
print()
|
||||||
|
print(transformer.lists)
|
||||||
|
print()
|
||||||
|
[print(c) for c in transformer.courses]
|
||||||
|
print()
|
||||||
|
print("\nDEGREES: ")
|
||||||
|
for d,i in transformer.degrees.items():
|
||||||
|
print(str(i))
|
||||||
|
|
||||||
|
print()
|
||||||
|
[print(d) for d in transformer.lists]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def rule_check_units():
|
||||||
|
# Define the course units and passing grades
|
||||||
|
course_units = {
|
||||||
|
"A": 1,
|
||||||
|
"B": 2,
|
||||||
|
"C": 1,
|
||||||
|
"D": 3
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define the students and their passed courses
|
||||||
|
students = {
|
||||||
|
"Peter": ["A", "B", "C"],
|
||||||
|
"Mark": ["D", "B"],
|
||||||
|
"Susan": ["A", "B"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define the minimum units required to graduate
|
||||||
|
min_units_to_graduate = 4
|
||||||
|
|
||||||
|
calcs = {}
|
||||||
|
for student, passed_courses in students.items():
|
||||||
|
print("\n", student, passed_courses)
|
||||||
|
for c in passed_courses:
|
||||||
|
print(f"\t{c:15} {course_units[c]}")
|
||||||
|
print(f"\t{'Total':15} {sum(course_units[c] for c in passed_courses)}")
|
||||||
|
|
||||||
|
# Determine the graduating students
|
||||||
|
graduating_students = [
|
||||||
|
student for student, passed_courses in students.items()
|
||||||
|
if sum(course_units[course] for course in passed_courses) >= min_units_to_graduate
|
||||||
|
]
|
||||||
|
|
||||||
|
# Print the graduating students
|
||||||
|
if graduating_students:
|
||||||
|
print("The graduating students are:", ", ".join(graduating_students))
|
||||||
|
else:
|
||||||
|
print("No students meet the graduation requirements.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def compute_degree_progress(student_courses, degree):
|
||||||
|
total_units_completed = 0
|
||||||
|
total_required_units = 0
|
||||||
|
for course in degree['required_courses']:
|
||||||
|
if course['id'] in student_courses:
|
||||||
|
total_units_completed += course['units']
|
||||||
|
if 'additional_rules' in course:
|
||||||
|
for rule in course['additional_rules']:
|
||||||
|
if rule['type'] == 'at_least_n_units_from_list':
|
||||||
|
units_from_list = sum([c['units'] for c in degree['courses'] if c['id'] in student_courses and c['id'] in rule['course_list']])
|
||||||
|
if units_from_list < rule['n']:
|
||||||
|
break
|
||||||
|
total_required_units += course['units']
|
||||||
|
progress_percent = total_units_completed / total_required_units * 100 if total_required_units > 0 else 0
|
||||||
|
return progress_percent
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
word_to_num = {'course':1, 'One':1, 'one':1, 'two':2, 'three':3, 'four':4, 'five':5, 'six':6,
|
||||||
|
'seven':7, 'eight':8, 'nine':9, 'ten':10, 'eleven':11,}
|
||||||
|
|
||||||
|
def word2num(word, verbose=0):
|
||||||
|
word = word.lower()
|
||||||
|
ret = word_to_num[word] if word in word_to_num else word
|
||||||
|
if verbose:
|
||||||
|
print(f" word2num({word}) -> {ret}")
|
||||||
|
return ret
|
||||||
|
|
||||||
|
lab_classes = {}
|
||||||
|
|
||||||
|
def load_lab_classes():
|
||||||
|
global lab_classes
|
||||||
|
if lab_classes:
|
||||||
|
return lab_classes
|
||||||
|
for c in json.loads(codecs.open('cache/courses/courses_built.json','r','utf-8').read()).values():
|
||||||
|
#print(c)
|
||||||
|
if 'min_lab_hour' in c and float(c['min_lab_hour']) > 0:
|
||||||
|
lab_classes[c['dept'] + c['number']] = 1
|
||||||
|
print(lab_classes)
|
||||||
|
return lab_classes
|
||||||
|
|
||||||
|
def is_lab_class(c):
|
||||||
|
lab_classes = load_lab_classes()
|
||||||
|
if c in lab_classes:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_noncourse_new_section(noncourse_line):
|
||||||
|
from degree_vars import note_true_section, note_false_section
|
||||||
|
for non in note_false_section:
|
||||||
|
if re.search(non, noncourse_line):
|
||||||
|
#print(f"- {noncourse_line}")
|
||||||
|
return False
|
||||||
|
for yes in note_true_section:
|
||||||
|
if re.search(yes, noncourse_line):
|
||||||
|
#print(f"+ {noncourse_line}")
|
||||||
|
return True
|
||||||
|
print(f" -> should this start a new rule/section? [{noncourse_line}]")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
rule_lookup = {
|
||||||
|
'take_all_prereq': ['RN PROGRAM PREREQUISITES', 'PREREQUISITES', ],
|
||||||
|
'take at least n courses': ['(\d+) courses total', 'SELECT (ONE|TWO) OF THE FOLLOWING', 'Select (one|two|three)', 'Select (\d+) courses',
|
||||||
|
'Choose (one) or more', 'Choose (one|two|three) of the classes listed',
|
||||||
|
'Choose (\w+) of the following','Choose (one|two|three)', 'Choose ([\d\w]+) courses from',
|
||||||
|
'ANY (COURSE) NOT USED IN', 'Select (1)', 'Select (one) of the following REQUIRED CORE',
|
||||||
|
'(One) of the following:', 'LIST [AB]: Select (\d)', 'Choose (One) Course:', ],
|
||||||
|
'take at least n units': ['LIST A \((\d+) units\)', 'LIST B \((\d+) units\)', 'LIST C \- Any course .*\((\d+) units\)', '(\d+) units total', 'Select (\d+) units', 'Any combination totaling (\d+) units',
|
||||||
|
'Choose (\w+) units from classes listed', 'Choose a minimum of ([\w\d]+) units from',
|
||||||
|
'Choose any combination of courses for a minimum of ([\w\d]+) units',
|
||||||
|
'Choose ([\w\d]+) units', 'Choose courses for at least ([\w\d]+) units',
|
||||||
|
'Choose a minimum of (\d+) units', 'Select any (\d+)\-\d+ units from the following'],
|
||||||
|
'electives': ['Electives', 'Recommended electives?:', ],
|
||||||
|
'take_all': ['RN PROGRAM', 'REQUIRED CORE', 'CORE COURSES', 'ADDITIONAL REQUIREMENTS','REQUIREMENTS:', 'Requirements', 'Core Requirements',
|
||||||
|
'Required Core', 'REQUIRED', 'LVN PROGRAM', 'Student Teaching Practicum', '^LIST A:?$', '^LIST B:$',
|
||||||
|
'Program Requirements', 'Required Courses:', 'PROGRAM REQUIREMENTS (5 Units)', 'PROGRAM REQUIREMENTS (162 Hours)',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
def lookup_rule(line):
|
||||||
|
verbose = 0
|
||||||
|
for key in rule_lookup.keys():
|
||||||
|
for each in rule_lookup[key]:
|
||||||
|
m = re.search(each, line)
|
||||||
|
if m:
|
||||||
|
num = None
|
||||||
|
try:
|
||||||
|
if m.group(1):
|
||||||
|
num = m.group(1)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
if verbose: print(f"line: {line} matched: {each} with {num}")
|
||||||
|
return key,num
|
||||||
|
return None,None
|
||||||
|
|
||||||
|
def examine(li,award, verbose=0):
|
||||||
|
summary = [x[0] for x in li]
|
||||||
|
if summary[1] in ['and','or']:
|
||||||
|
if verbose: print(" - ", summary)
|
||||||
|
|
||||||
|
|
||||||
|
def check_ands_ors_pbd(award, pbd, verbose=0):
|
||||||
|
verbose = 0
|
||||||
|
if verbose: print(f"check_ands_ors_pbd({award}, ...)")
|
||||||
|
summary = [x[0] for x in pbd]
|
||||||
|
if verbose: print(" ", summary)
|
||||||
|
|
||||||
|
# iterate through in groups of 3, from 0/1/2 up to 3/4/5
|
||||||
|
# (for length 6. length n: n-2, n-1, n)
|
||||||
|
for i in range(len(pbd)-2):
|
||||||
|
examine(pbd[i:i+3], award, verbose)
|
||||||
|
if verbose: print()
|
||||||
|
|
||||||
|
|
||||||
|
def build_catalog():
|
||||||
|
cfile = "cache/courses/courses_active_built.json"
|
||||||
|
pfile = "cache/programs/programs_built.json"
|
||||||
|
|
||||||
|
courses = json.loads(codecs.open(cfile,'r','utf-8').read())
|
||||||
|
programs = json.loads(codecs.open(pfile,'r','utf-8').read())
|
||||||
|
|
||||||
|
course_spec = []
|
||||||
|
|
||||||
|
for index,c in courses.items():
|
||||||
|
try:
|
||||||
|
d = c['dept']
|
||||||
|
n = c['number']
|
||||||
|
name = c['name']
|
||||||
|
u2 = num_units(c['min_units'])
|
||||||
|
if 'max_units' in c:
|
||||||
|
u1 = num_units(c['max_units'])
|
||||||
|
else:
|
||||||
|
u1 = u2
|
||||||
|
|
||||||
|
if u1 == u2:
|
||||||
|
units = {'units': u1}
|
||||||
|
u = u1
|
||||||
|
else:
|
||||||
|
units = {'min_units': u2, 'max_units': u1}
|
||||||
|
u = f"{u2}-{u1}"
|
||||||
|
|
||||||
|
course_spec.append(f"course {d}{n} {u} units")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
#print(e)
|
||||||
|
#print(json.dumps(c,indent=2))
|
||||||
|
|
||||||
|
d_out = codecs.open('cache/prog_debug.txt','w','utf-8')
|
||||||
|
def d(s): d_out.write(str(s) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
for index,p in programs.items():
|
||||||
|
v2 = 0 # print debugging stuff
|
||||||
|
# Each award (degree or certificate)
|
||||||
|
award = p['award'] + " " + p['program_title']
|
||||||
|
d("\n" + p['award'] + " " + p['program_title'])
|
||||||
|
print("\n" + award)
|
||||||
|
this_program = p['award'] + " " + p['program_title']
|
||||||
|
this_rule = ""
|
||||||
|
r = p['requirements']
|
||||||
|
course_count = 0
|
||||||
|
|
||||||
|
# Each numbered chunk (k) in the requirements section
|
||||||
|
for k in sorted(r.keys()):
|
||||||
|
|
||||||
|
# Each 'program block definition'
|
||||||
|
# 1st is dict with unit totals, rest are lists.
|
||||||
|
|
||||||
|
check_ands_ors_pbd( award, sorted( r[k][1:], key=lambda x: float(x[1])) )
|
||||||
|
for each_r in sorted( r[k][1:], key=lambda x: float(x[1])):
|
||||||
|
|
||||||
|
if each_r[0] in ['and','or']:
|
||||||
|
#print(' ', each_r[0],each_r[1])
|
||||||
|
pass
|
||||||
|
if isinstance(each_r, list):
|
||||||
|
#print(each_r)
|
||||||
|
if each_r[0] == 'h3' or (each_r[0]=='noncourse' and is_noncourse_new_section(each_r[2])):
|
||||||
|
# This is a rule title
|
||||||
|
if this_rule and course_count:
|
||||||
|
d(this_rule) # + f" ({course_count}) "
|
||||||
|
course_count = 0
|
||||||
|
raw_rule = each_r[2]
|
||||||
|
good_rule, num = lookup_rule(raw_rule)
|
||||||
|
if good_rule:
|
||||||
|
#print(f"\t{good_rule}")
|
||||||
|
n = word2num(num) if num else ""
|
||||||
|
if v2: print(f"\tn = {n}")
|
||||||
|
actual_rule = re.sub(r'\sn\s',f' {n} ',good_rule)
|
||||||
|
if v2: print(f"\tactual rule is: {actual_rule}")
|
||||||
|
#this_rule = f"{good_rule} ({n}) [{raw_rule}] from "
|
||||||
|
this_rule = f"{actual_rule} from "
|
||||||
|
else:
|
||||||
|
#print(f"\t{raw_rule}")
|
||||||
|
if not this_rule:
|
||||||
|
this_rule = "take_all from "
|
||||||
|
this_rule = " * " + raw_rule + " " + "from "
|
||||||
|
#elif each_r[0] == 'noncourse': # also a rule title, some kind of sub-rule?
|
||||||
|
# d( f" ++ (noncourse) {each_r[2]}")
|
||||||
|
elif each_r[0] == 'course': # course in a rule
|
||||||
|
if not this_rule:
|
||||||
|
this_rule = "take_all from "
|
||||||
|
is_lab = '[L]' if is_lab_class(each_r[2]['code']) else ''
|
||||||
|
#this_rule += f"{each_r[2]['code']}{is_lab}({each_r[1]}), "
|
||||||
|
this_rule += f"{each_r[2]['code']}{is_lab}, "
|
||||||
|
if v2: print(f"\t\tthis rule is now: {this_rule}")
|
||||||
|
course_count += 1
|
||||||
|
if course_count:
|
||||||
|
d(this_rule) # + f" ({course_count})"
|
||||||
|
|
||||||
|
|
||||||
|
d_out.close()
|
||||||
|
d_in = codecs.open('cache/prog_debug.txt','r','utf-8').readlines()
|
||||||
|
progs = []
|
||||||
|
this_prog = []
|
||||||
|
for line in d_in:
|
||||||
|
if line.strip() == '':
|
||||||
|
if this_prog:
|
||||||
|
progs.append(this_prog)
|
||||||
|
this_prog = []
|
||||||
|
else:
|
||||||
|
this_prog.append(line.strip())
|
||||||
|
|
||||||
|
okay = []
|
||||||
|
notokay = []
|
||||||
|
for p in progs:
|
||||||
|
ok =1
|
||||||
|
for line in p:
|
||||||
|
if line[0] == '*':
|
||||||
|
notokay.append(p)
|
||||||
|
ok = 0
|
||||||
|
continue
|
||||||
|
if ok:
|
||||||
|
okay.append(p)
|
||||||
|
|
||||||
|
print("These programs are okay:")
|
||||||
|
for p in okay:
|
||||||
|
for l in p:
|
||||||
|
print(l)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("\n\nThese programs are not okay:")
|
||||||
|
for p in notokay:
|
||||||
|
for l in p:
|
||||||
|
print(l)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print(f"okay: {len(okay)}")
|
||||||
|
print(f"not okay: {len(notokay)}")
|
||||||
|
return
|
||||||
|
do_parse('\n'.join(course_spec))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## TODO or and and
|
||||||
|
## How to see this as a 'fake' course: Eligible for English 1A ?
|
||||||
|
|
||||||
|
## How to include this as part of a list: 'ANY COURSE NOT USED IN LIST A (3 units)'
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
options = { 1: ['parsing example',parser] ,
|
||||||
|
2: ['rule check by units',rule_check_units] ,
|
||||||
|
3: ['load all programs',build_catalog] ,
|
||||||
|
}
|
||||||
|
print ('')
|
||||||
|
|
||||||
|
if len(sys.argv) > 1 and re.search(r'^\d+',sys.argv[1]):
|
||||||
|
resp = int(sys.argv[1])
|
||||||
|
print("\n\nPerforming: %s\n\n" % options[resp][0])
|
||||||
|
|
||||||
|
else:
|
||||||
|
print ('')
|
||||||
|
for key in options:
|
||||||
|
print(str(key) + '.\t' + options[key][0])
|
||||||
|
|
||||||
|
print('')
|
||||||
|
resp = input('Choose: ')
|
||||||
|
|
||||||
|
# Call the function in the options dict
|
||||||
|
options[ int(resp)][1]()
|
||||||
|
|
||||||
81
stats.py
81
stats.py
|
|
@ -500,7 +500,7 @@ def reorganize_grades_student():
|
||||||
output.writerow(row)
|
output.writerow(row)
|
||||||
|
|
||||||
|
|
||||||
def all_course_names():
|
def all_course_names_setup():
|
||||||
cc = json.loads(codecs.open('cache/courses/courses_built.json','r','utf-8').read())
|
cc = json.loads(codecs.open('cache/courses/courses_built.json','r','utf-8').read())
|
||||||
courses = {}
|
courses = {}
|
||||||
for C in cc.values():
|
for C in cc.values():
|
||||||
|
|
@ -533,6 +533,82 @@ def all_course_names():
|
||||||
#co.write( json.dumps( {'unknown':unknown, 'coursenames':courses}, indent=2 ))
|
#co.write( json.dumps( {'unknown':unknown, 'coursenames':courses}, indent=2 ))
|
||||||
|
|
||||||
|
|
||||||
|
lookup = {}
|
||||||
|
names = {}
|
||||||
|
|
||||||
|
def shell2course(shell):
|
||||||
|
global lookup, names
|
||||||
|
if not lookup:
|
||||||
|
cr = json.loads(codecs.open('cache/courses/names.json','r','utf-8').read())
|
||||||
|
lookup = cr['unknown']
|
||||||
|
allcourses = cr['coursenames']
|
||||||
|
names = allcourses.keys()
|
||||||
|
|
||||||
|
if shell in names:
|
||||||
|
return shell
|
||||||
|
if shell in lookup:
|
||||||
|
c = lookup[shell]
|
||||||
|
if c in names:
|
||||||
|
return c
|
||||||
|
#print(f"Can't find course: {shell}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def stu_record_line(line):
|
||||||
|
line = line.strip()
|
||||||
|
parts = line.split(',')
|
||||||
|
stu_id = parts[0]
|
||||||
|
courses = []
|
||||||
|
for C in parts[1:]:
|
||||||
|
courses.append(C.split('|'))
|
||||||
|
return stu_id, courses
|
||||||
|
|
||||||
|
def stu_record_to_vector(line):
|
||||||
|
id, courses = stu_record_line(line)
|
||||||
|
|
||||||
|
template = json.loads(codecs.open('cache/courses/course_main_record.json','r','utf-8').read())
|
||||||
|
lookup = {}
|
||||||
|
for i,c in enumerate(template):
|
||||||
|
lookup[c] = i
|
||||||
|
vector = [0 for x in range(len(template))]
|
||||||
|
for C in courses:
|
||||||
|
goodname = shell2course(C[0])
|
||||||
|
if goodname:
|
||||||
|
vector[lookup[goodname]] = 1 # C[1] # score
|
||||||
|
return id,vector
|
||||||
|
|
||||||
|
|
||||||
|
def grades_to_vectors():
|
||||||
|
grades = codecs.open('cache/courses_student_scores.csv','r','utf-8').readlines()
|
||||||
|
for L in grades:
|
||||||
|
id, vector = stu_record_to_vector(L)
|
||||||
|
print(id, vector)
|
||||||
|
|
||||||
|
|
||||||
|
def all_course_names():
|
||||||
|
|
||||||
|
|
||||||
|
complete_list = {}
|
||||||
|
missing_names = {}
|
||||||
|
with open(student_courses_scores,'r') as input_f:
|
||||||
|
for L in input_f:
|
||||||
|
stu_id, courses = stu_record_line(L)
|
||||||
|
for C in courses:
|
||||||
|
real_name = shell2course(C[0])
|
||||||
|
if real_name:
|
||||||
|
complete_list[real_name] = 1
|
||||||
|
else:
|
||||||
|
missing_names[C[0]] = 1
|
||||||
|
master_record = sorted(complete_list.keys())
|
||||||
|
print(f"Found {len(master_record)} courses")
|
||||||
|
print(master_record)
|
||||||
|
print(f"Missing {len(missing_names)} courses")
|
||||||
|
print(missing_names)
|
||||||
|
mr = codecs.open('cache/courses/course_main_record.json','w','utf-8')
|
||||||
|
mr.write(json.dumps(master_record,indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -544,7 +620,8 @@ if __name__ == "__main__":
|
||||||
4: ['test shortname parse',nametest] ,
|
4: ['test shortname parse',nametest] ,
|
||||||
5: ['test sem codes',codetest] ,
|
5: ['test sem codes',codetest] ,
|
||||||
6: ['get student data from orientations', get_student_orientations],
|
6: ['get student data from orientations', get_student_orientations],
|
||||||
7: ['manage course master list', all_course_names]
|
7: ['manage course master list', all_course_names],
|
||||||
|
8: ['grades to vectors', grades_to_vectors]
|
||||||
}
|
}
|
||||||
print ('')
|
print ('')
|
||||||
|
|
||||||
|
|
|
||||||
19
tasks.py
19
tasks.py
|
|
@ -17,7 +17,7 @@ import pysftp, os, datetime, requests, re, json, sqlite3, codecs, csv, sys
|
||||||
import funcy, os.path, shutil, urllib
|
import funcy, os.path, shutil, urllib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import strptime
|
#from datetime import strptime
|
||||||
from time import mktime
|
from time import mktime
|
||||||
|
|
||||||
from canvas_secrets import badgr_target, badgr_hd
|
from canvas_secrets import badgr_target, badgr_hd
|
||||||
|
|
@ -30,7 +30,7 @@ if os.name != 'posix':
|
||||||
from docxtpl import DocxTemplate
|
from docxtpl import DocxTemplate
|
||||||
import xlwt
|
import xlwt
|
||||||
|
|
||||||
from pipelines import header, url, fetch, convert_roster_files, move_to_folder, fetch_dict
|
from pipelines import header, url, fetch, convert_roster_files, move_to_folder
|
||||||
from courses import course_enrollment
|
from courses import course_enrollment
|
||||||
from users import teacherRolesCache
|
from users import teacherRolesCache
|
||||||
from util import match59, partition
|
from util import match59, partition
|
||||||
|
|
@ -1303,8 +1303,19 @@ def file_renamer():
|
||||||
os.rename( where+F, where+nn )
|
os.rename( where+F, where+nn )
|
||||||
print("ok")
|
print("ok")
|
||||||
|
|
||||||
|
def list_auth():
|
||||||
|
r = fetch( url + '/api/v1/accounts/1/authentication_providers')
|
||||||
|
print(json.dumps(r,indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def update_auth():
|
||||||
|
#r = fetch( url + '/api/v1/accounts/1/authentication_providers')
|
||||||
|
u = url + '/api/v1/accounts/1/authentication_providers/104'
|
||||||
|
opt = {"metadata_uri": r'https://eis-prod.ec.gavilan.edu/saml/idp-metadataxxx.xml'}
|
||||||
|
r2 = requests.put(u, headers=header, data=opt)
|
||||||
|
print ( r2.text )
|
||||||
|
|
||||||
|
#print(json.dumps(r,indent=2))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
@ -1318,6 +1329,8 @@ if __name__ == "__main__":
|
||||||
8: ['collate all profile pics for db',index_pics] ,
|
8: ['collate all profile pics for db',index_pics] ,
|
||||||
9: ['process schedule csv file from web',parse_schedule] ,
|
9: ['process schedule csv file from web',parse_schedule] ,
|
||||||
10: ['dumb rename images mistake',file_renamer] ,
|
10: ['dumb rename images mistake',file_renamer] ,
|
||||||
|
11: ['list auth', list_auth],
|
||||||
|
12: ['update auth', update_auth],
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sys.argv) > 1 and re.search(r'^\d+',sys.argv[1]):
|
if len(sys.argv) > 1 and re.search(r'^\d+',sys.argv[1]):
|
||||||
|
|
|
||||||
14
zinc.py
14
zinc.py
|
|
@ -1,16 +1,16 @@
|
||||||
from minizinc import Instance, Model, Solver
|
from minizinc import Instance, Model, Solver
|
||||||
|
|
||||||
# Load
|
# Load n-Queens model from file
|
||||||
nqueens = Model("cache/playground.mzn")
|
nqueens = Model("./cache/sample_degree_check.mzn")
|
||||||
|
#nqueens = Model("./cache/nqueens.mzn")
|
||||||
# Find the MiniZinc solver configuration for Gecode
|
# Find the MiniZinc solver configuration for Gecode
|
||||||
gecode = Solver.lookup("gecode")
|
gecode = Solver.lookup("gecode")
|
||||||
# Create an Instance of the n-Queens model for Gecode
|
# Create an Instance of the n-Queens model for Gecode
|
||||||
instance = Instance(gecode, nqueens)
|
instance = Instance(gecode, nqueens)
|
||||||
# Assign 4 to n
|
# Assign 4 to n
|
||||||
#instance["n"] = 4
|
#instance["n"] = 4
|
||||||
#result = instance.solve()
|
|
||||||
result = instance.solve(all_solutions=True)
|
result = instance.solve()
|
||||||
# Output the array q
|
# Output the array q
|
||||||
#print(result)
|
print(result)
|
||||||
for i in range(len(result)):
|
#print(result["rule_1_needed","rule_2_needed"])
|
||||||
print(result[i])
|
|
||||||
Loading…
Reference in New Issue