165 lines
4.4 KiB
Python
165 lines
4.4 KiB
Python
from lark import Lark, Transformer, v_args
|
|
|
|
|
|
|
|
|
|
"""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."""
|
|
|
|
|
|
|
|
|
|
grammar = """
|
|
start: program
|
|
|
|
program: course_declaration*
|
|
|
|
course_declaration: "course" CNAME INT "prerequisites" (CNAME ","?)* "degree" CNAME "{" degree_rule* "}"
|
|
|
|
degree_rule: "take" ("all" | "at" INT "from") ("the" "following") "courses" course_list
|
|
|
|
course_list: (CNAME ","?)*
|
|
|
|
%import common.CNAME
|
|
%import common.INT
|
|
%import common.WS
|
|
%ignore WS
|
|
"""
|
|
|
|
class Course:
|
|
def __init__(self, name, units, prerequisites, degree):
|
|
self.name = name
|
|
self.units = units
|
|
self.prerequisites = prerequisites
|
|
self.degree = degree
|
|
|
|
class DegreeRule:
|
|
def __init__(self, verb, n, course_list):
|
|
self.verb = verb
|
|
self.n = n
|
|
self.course_list = course_list
|
|
|
|
class Degree:
|
|
def __init__(self, name, degree_rules):
|
|
self.name = name
|
|
self.degree_rules = degree_rules
|
|
|
|
class Rule:
|
|
pass
|
|
|
|
class TakeAll(Rule):
|
|
def __init__(self, courses):
|
|
self.courses = courses
|
|
|
|
class TakeN(Rule):
|
|
def __init__(self, courses, n):
|
|
self.courses = courses
|
|
self.n = n
|
|
|
|
class TakeNUnits(Rule):
|
|
def __init__(self, courses, n):
|
|
self.courses = courses
|
|
self.n = n
|
|
|
|
@v_args(inline=True)
|
|
class DSLTransformer(Transformer):
|
|
def __init__(self):
|
|
self.courses = {}
|
|
self.degrees = {}
|
|
|
|
def course_declaration(self, name, units, prerequisites, degree):
|
|
return Course(name, int(units), prerequisites.children, degree)
|
|
|
|
def degree(self, name, required_courses):
|
|
self.degrees[name] = Degree(name, required_courses)
|
|
|
|
def take_all(self, courses):
|
|
return TakeAll(courses)
|
|
|
|
def take_n(self, courses, n):
|
|
return TakeN(courses, n)
|
|
|
|
def take_n_units(self, courses, n):
|
|
return TakeNUnits(courses, n)
|
|
|
|
def degree_rule(self, items):
|
|
verb = items[0]
|
|
n = None
|
|
course_list = items[-1]
|
|
if len(items) == 4:
|
|
n = int(items[2])
|
|
return DegreeRule(verb, n, course_list.children)
|
|
|
|
def course_list(self, items):
|
|
return items[:-1]
|
|
|
|
def program(self, items):
|
|
courses = [item for item in items if isinstance(item, Course)]
|
|
degrees = [item for item in items if isinstance(item, Degree)]
|
|
return courses, degrees
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
parser = Lark(grammar)
|
|
transformer = DSLTransformer()
|
|
|
|
def parse_dsl(dsl):
|
|
tree = parser.parse(dsl)
|
|
return transformer.transform(tree)
|
|
|
|
dsl = """
|
|
course CS101 3 prerequisites none degree CS {
|
|
take all the following courses {
|
|
CS102, CS103
|
|
}
|
|
take at least 6 from {
|
|
MATH101, MATH102, MATH103
|
|
}
|
|
}
|
|
"""
|
|
|
|
courses, degrees = parse_dsl(dsl)
|
|
print(courses)
|
|
print(degrees)
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|