still automating degree checking

This commit is contained in:
Coding with Peter 2023-06-05 15:18:14 -07:00
parent 164541d826
commit 403cfc533b
5 changed files with 857 additions and 233 deletions

View File

@ -1,6 +1,15 @@
from lark import Lark, Transformer, v_args
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
@ -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)
- 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
@ -45,68 +28,46 @@ class CourseOr:
class Course:
def __init__(self, name, units, prerequisites=[]):
def __init__(self, name):
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):
def __init__(self, name, courses):
self.name = name
self.course_list = course_list
self.l = len(course_list)
self.courses = courses
def __iter__(self):
return iter(self.course_list)
return iter(self.courses)
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)
return len(self.courses)
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})"
return ", ".join([f"\"{str(x)}\"" for x in self.courses])
class DegreeRule:
rule_count = 1
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}];"
self.rule_number = DegreeRule.rule_count
DegreeRule.rule_count += 1
def __repr__(self):
return self.mz_rule(1)
#return f"DegreeRule({self.rule_type}, {self.course_list})"
s = ",".join( [ f"{x}" for x in 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:
def __init__(self, name, degree_rules):
self.name = name
self.degree_rules = degree_rules
def mz_rule(self):
def __repr__(self):
n = 1
s = f"% Degree: {self.name}\n"
for r in self.degree_rules:
s += r.mz_rule(n) + "\n"
s += str(r) + "\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):
@ -143,94 +104,70 @@ class DSLTransformer(Transformer):
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)
d("\ncourse_declaration")
d(name)
d(units)
def take_all(self):
d("\ntake_all")
return TakeAll()
def take_n_courses(self, n):
d("\ntake_n_courses")
d(n)
return TakeN(n)
def take_n_units(self, n):
d("\ntake_n_units")
d(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 degree_rule(self, rule_type, course_list):
d("\ndegree_rule")
d(rule_type)
d(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]
d("\nlist_ref")
d(name)
return self.lists[name]
def course(self, *items):
return Course(items[0].value,"")
def course(self, c):
d("\ncourse")
d(c)
return Course(c.value)
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):
#print(str(items))
print("course list: ", items)
return CourseList("temp", items)
#return [c for c in items]
if items[-1] == None: items = items[:-1]
d("\ncourse_list")
d(items)
return 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)
"""
def list_declaration(self, name, course_list):
d("\nlist_declaration")
d(name)
d(course_list)
c = CourseList(name, course_list)
self.lists[name] = c
return c
def program(self, name, *rules):
d("\nprogram")
d(name)
d(rules)
dg = Degree(name, rules)
self.degrees[name] = dg
return dg
#(course ","?)*
grammar = """
start: _spec+
@ -273,25 +210,9 @@ grammar = """
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
@ -312,11 +233,11 @@ def parser():
def do_parse(dsl):
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)
transformer = DSLTransformer()
@ -337,53 +258,11 @@ def do_parse(dsl):
[print(c) for c in transformer.courses]
print()
print("\nDEGREES: ")
for d,i in transformer.degrees.items():
for deg,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.")
[print(deg) for deg in transformer.lists]
@ -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,
'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()
def build_catalog():
def build_program_rules():
cfile = "cache/courses/courses_active_built.json"
pfile = "cache/programs/programs_built.json"
@ -630,7 +488,7 @@ def build_catalog():
if ok:
okay.append(p)
print("These programs are okay:")
print("\n\n\n\nThese programs are okay:")
for p in okay:
for l in p:
print(l)
@ -646,20 +504,16 @@ def build_catalog():
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] ,
2: ['build program rules', build_program_rules],
}
print ('')

680
degrees_take1.py Normal file
View File

@ -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]()

View File

@ -500,7 +500,7 @@ def reorganize_grades_student():
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())
courses = {}
for C in cc.values():
@ -533,6 +533,82 @@ def all_course_names():
#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] ,
5: ['test sem codes',codetest] ,
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 ('')

View File

@ -17,7 +17,7 @@ import pysftp, os, datetime, requests, re, json, sqlite3, codecs, csv, sys
import funcy, os.path, shutil, urllib
from datetime import datetime
from collections import defaultdict
from datetime import strptime
#from datetime import strptime
from time import mktime
from canvas_secrets import badgr_target, badgr_hd
@ -30,7 +30,7 @@ if os.name != 'posix':
from docxtpl import DocxTemplate
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 users import teacherRolesCache
from util import match59, partition
@ -1303,8 +1303,19 @@ def file_renamer():
os.rename( where+F, where+nn )
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__":
@ -1318,6 +1329,8 @@ if __name__ == "__main__":
8: ['collate all profile pics for db',index_pics] ,
9: ['process schedule csv file from web',parse_schedule] ,
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]):

14
zinc.py
View File

@ -1,16 +1,16 @@
from minizinc import Instance, Model, Solver
# Load
nqueens = Model("cache/playground.mzn")
# Load n-Queens model from file
nqueens = Model("./cache/sample_degree_check.mzn")
#nqueens = Model("./cache/nqueens.mzn")
# Find the MiniZinc solver configuration for Gecode
gecode = Solver.lookup("gecode")
# Create an Instance of the n-Queens model for Gecode
instance = Instance(gecode, nqueens)
# Assign 4 to n
#instance["n"] = 4
#result = instance.solve()
result = instance.solve(all_solutions=True)
result = instance.solve()
# Output the array q
#print(result)
for i in range(len(result)):
print(result[i])
print(result)
#print(result["rule_1_needed","rule_2_needed"])