def test_audit__double_history_and_studio(): student: Dict[str, Any] = { 'areas': [ AreaPointer( code='140', status=AreaStatus.Declared, kind=AreaType.Major, name='Studio Art', degree='B.A.', dept='ART', gpa=None, terms_since_declaration=None, ), AreaPointer( code='135', status=AreaStatus.Declared, kind=AreaType.Major, name='Art History', degree='B.A.', dept='ART', gpa=None, terms_since_declaration=None, ), ], 'courses': [ course_from_str('DEPT 123'), ], } c = Constants(matriculation_year=2000) area = AreaOfStudy.load(c=c, student=Student.load( dict(areas=student['areas'], courses=student['courses'])), specification={ 'name': 'Art History Test', 'type': 'major', 'code': '140', 'degree': 'B.A.', 'result': { 'all': [{ 'course': 'DEPT 123' }], } }) messages = list( audit(area=area, student=Student.load( dict(areas=student['areas'], courses=student['courses'])))) result = messages[-1].result assert result.result.items[-1].result.items[-1].result.assertions[ 0].expected == 18
def test_limit__at_most_1_course(): test_data = io.StringIO(""" limit: - at_most: 1 where: {number: {$eq: 201}} result: from: courses where: {number: {$eq: 201}} assert: {count(courses): {$gte: 1}} """) area = AreaOfStudy.load(specification=yaml.load(stream=test_data, Loader=yaml.SafeLoader), c=c) course_1 = course_from_str("BIO 201") course_2 = course_from_str("ABC 201") transcript = [course_1, course_2] solutions = list(area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) course_sets = set(frozenset(s.solution.output) for s in solutions) assert course_sets == set([ frozenset((course_2,)), frozenset((course_1,)), frozenset(()), ])
def test_limit__at_most_1_credit(): test_data = io.StringIO(""" limit: - at_most: 1 credit where: {number: {$eq: 201}} result: from: courses assert: {count(courses): {$gte: 1}} """) area = AreaOfStudy.load(specification=yaml.load(stream=test_data, Loader=yaml.SafeLoader), c=c) course_1 = course_from_str("ABC 201", credits='0.5') course_2 = course_from_str("BCD 201", credits='0.5') course_3 = course_from_str("CDE 201", credits='0.5') transcript = [course_1, course_2, course_3] solutions = list(area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) course_sets = set(frozenset(s.solution.output) for s in solutions) for i, s in enumerate(course_sets): print(i) for _c in s: print(_c.course()) assert course_sets == set([ frozenset((course_1, course_2)), frozenset((course_1, course_3)), frozenset((course_2, course_3)), frozenset((course_1,)), frozenset((course_2,)), frozenset((course_3,)), frozenset(()), ])
def test_from(caplog): caplog.set_level(logging.DEBUG) test_data = io.StringIO(""" result: from: courses where: {gereqs: {$eq: SPM}} assert: {count(courses): {$gte: 1}} """) area = AreaOfStudy.load(specification=yaml.load(stream=test_data, Loader=yaml.SafeLoader), c=c) transcript = [ course_from_str("CSCI 111", gereqs=['SPM'], year=2008, term='1'), course_from_str("ASIAN 110"), ] s = next( area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) a = s.audit().result assert len(a.successful_claims) == 1 assert a.successful_claims[0].course.clbid == transcript[0].clbid
def test_solution_count_greaterthan_1(caplog): caplog.set_level(logging.DEBUG, logger='dp.rule.given.rule') area, transcript = __get_data(""" result: from: courses where: {gereqs: {$eq: SPM}} assert: {count(courses): {$gt: 1}} """) solutions = area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[]) sol = next(solutions) assert len(sol.solution.output) == 2 sol = next(solutions) assert len(sol.solution.output) == 2 sol = next(solutions) assert len(sol.solution.output) == 2 sol = next(solutions) assert len(sol.solution.output) == 3 with pytest.raises(StopIteration): next(solutions)
def test_limits_esth(caplog): spec = """ result: from: courses limit: - at_most: 1 where: $or: - course: {$in: ['STAT 110', 'STAT 212', 'STAT 214']} - ap: {$eq: AP Statistics} assert: {count(courses): {$gte: 2}} """ area = AreaOfStudy.load(specification=yaml.load(stream=spec, Loader=yaml.SafeLoader), c=c) psych_241 = course_from_str("PSYCH 241", clbid="0") stat_212 = course_from_str("STAT 212", clbid="1") ap_stat = course_from_str("STAT 0", name="AP Statistics", course_type="AP", clbid="2") transcript = [psych_241, stat_212, ap_stat] solutions = list( area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) course_sets = [list(s.solution.output) for s in solutions] assert course_sets == [ [psych_241], [psych_241, stat_212], [psych_241, ap_stat], ]
def run_one(args: argparse.Namespace) -> None: stnum = args.stnum catalog = args.catalog code = args.code with sqlite_connect(args.db, readonly=True) as conn: results = conn.execute( ''' SELECT input_data FROM server_data WHERE (stnum, catalog, code) = (:stnum, :catalog, :code) ''', { 'stnum': stnum, 'catalog': catalog, 'code': code }) record = results.fetchone() input_data: dict = json.loads(record['input_data']) areas = load_areas(args, [{'catalog': catalog, 'code': code}]) result_msg = audit((stnum, catalog, code), data=input_data, db=args.db, area_spec=areas[f"{catalog}/{code}"]) assert result_msg student = Student.load(input_data) print(render_result(student, json.loads(result_msg['result'])))
def test_excluded_req_gpa(): transcript = trns() area = AreaOfStudy.load(c=c, student=Student.load(dict(courses=transcript)), specification={ 'name': 'test', 'type': 'concentration', 'result': { 'all': [ {'requirement': 'compsci'}, {'requirement': 'art'}, ], }, 'requirements': { 'compsci': { 'result': { 'from': 'courses', 'where': {'subject': {'$eq': 'CSCI'}}, 'assert': {'count(courses)': {'$gte': 2}}, } }, 'art': { 'in_gpa': False, 'result': { 'from': 'courses', 'where': {'subject': {'$eq': 'ART'}}, 'assert': {'count(courses)': {'$gte': 1}}, }, }, }, }) solution = list(area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[]))[0] result = solution.audit() assert area.result.items[1].in_gpa is False assert set(result.matched_for_gpa()) == set([transcript[0], transcript[1]]) assert transcript[2] not in set(result.matched_for_gpa()) assert result.gpa() == Decimal('2.5')
def test_global_limits(caplog): caplog.set_level(logging.DEBUG) test_data = io.StringIO(""" limit: - at_most: 1 where: {level: {$eq: 200}} - at_most: 1 where: {level: {$eq: 300}} result: from: courses where: {subject: {$eq: BIO}} assert: {count(courses): {$gte: 1}} """) area = AreaOfStudy.load(specification=yaml.load(stream=test_data, Loader=yaml.SafeLoader), c=c) bio_101 = course_from_str("BIO 101") bio_201 = course_from_str("BIO 201") bio_202 = course_from_str("BIO 202") bio_301 = course_from_str("BIO 301") bio_302 = course_from_str("BIO 302") transcript = [bio_101, bio_201, bio_202, bio_301, bio_302] solutions = list( area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) course_sets = set([frozenset(s.solution.output) for s in solutions]) assert course_sets == set([ frozenset((bio_101, bio_201)), frozenset((bio_101, bio_201, bio_301)), frozenset((bio_101, bio_201, bio_302)), frozenset((bio_101, bio_202)), frozenset((bio_101, bio_202, bio_301)), frozenset((bio_101, bio_202, bio_301)), frozenset((bio_101, bio_202, bio_302)), frozenset((bio_101, bio_301)), frozenset((bio_101, bio_302)), frozenset((bio_101, )), frozenset((bio_201, bio_301)), frozenset((bio_201, bio_302)), frozenset((bio_201, )), frozenset((bio_202, bio_301)), frozenset((bio_202, bio_302)), frozenset((bio_202, )), frozenset((bio_301, )), frozenset((bio_302, )), ])
def test_normal_gpa(): transcript = trns() area = AreaOfStudy.load(c=c, student=Student.load(dict(courses=transcript)), specification={ 'name': 'test', 'type': 'concentration', 'result': { 'all': [ {'requirement': 'compsci'}, {'requirement': 'art'}, ], }, 'requirements': { 'compsci': { 'result': { 'from': 'courses', 'where': {'subject': {'$eq': 'CSCI'}}, 'assert': {'count(courses)': {'$gte': 2}}, } }, 'art': { 'result': { 'from': 'courses', 'where': {'subject': {'$eq': 'ART'}}, 'assert': {'count(courses)': {'$gte': 1}}, }, }, }, }) solution = list(area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[]))[0] result = solution.audit() assert set(result.matched()) == set(transcript) assert result.gpa() == Decimal('2.0')
def test_mc__none_2(caplog): caplog.set_level(logging.DEBUG, logger='dp.context') test_data = io.StringIO(""" result: either: - requirement: Root - requirement: Alt requirements: Root: result: course: DEPT 123 Alt: result: course: DEPT 123 multicountable: DEPT 123: - [Root] """) area = AreaOfStudy.load(specification=yaml.load(stream=test_data, Loader=yaml.SafeLoader), c=c) course = course_from_str('DEPT 123') solutions = area.solutions(student=Student.load(dict(courses=[course])), exceptions=[]) results = [s.audit() for s in solutions] assert len(results) == 3 result_a, result_b, result_c = results assert result_a.is_ok() is True assert result_b.is_ok() is True assert result_c.is_ok() is True assert list(c.course.course() for c in result_c.claims() if c.failed is False) == [course.course()] assert result_c.result.items[0].result.claim_attempt.failed is False assert result_c.result.items[1].result.claim_attempt.failed is True
def test_overlaps(caplog: Any) -> None: # caplog.set_level(logging.DEBUG) global c area = AreaOfStudy.load(c=c, specification={ "result": {"all": [ {"requirement": "Core"}, {"requirement": "Electives"}, ]}, "requirements": { "Core": { "result": {"all": [ {"course": "MUSIC 212"}, {"course": "MUSIC 214"}, ]}, }, "Electives": { "result": { "from": "courses", "where": { "$and": [ {"subject": {"$eq": "MUSIC"}}, {"level": {"$eq": [300]}}, ], }, "all": [ {"assert": {"count(courses)": {"$gte": 2}}}, ], }, }, }, }) transcript = [course_from_str(c) for c in ["MUSIC 212", "MUSIC 214", "MUSIC 301", "MUSIC 302"]] ctx = RequirementContext().with_transcript(transcript) area.result.find_independent_children(items=area.result.items, ctx=ctx) solutions = list(area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) assert len(solutions) == 1 result = solutions[0].audit() assert result.is_ok() is True
def test_mc__only_query_references(caplog): caplog.set_level(logging.DEBUG, logger='dp.context') test_data = io.StringIO(""" result: all: - requirement: Root - requirement: Alt requirements: Root: result: from: courses assert: {count(courses): {$gte: 1}} Alt: result: from: courses assert: {count(courses): {$gte: 1}} multicountable: DEPT 123: - [Root] - [Alt] """) area = AreaOfStudy.load(specification=yaml.load(stream=test_data, Loader=yaml.SafeLoader), c=c) course = course_from_str('DEPT 123') solutions = area.solutions(student=Student.load(dict(courses=[course])), exceptions=[]) results = [s.audit() for s in solutions] assert len(results) == 1 result_a = results[0] assert result_a.is_ok() is True assert list(c.course.course() for c in result_a.claims() if c.failed is False) == [course.course(), course.course()]
def test_pruning_on_count_rule(caplog): caplog.set_level(logging.DEBUG) area = AreaOfStudy.load(specification={ "result": { "any": [ { "course": "DEPT 123" }, { "course": "DEPT 234" }, { "course": "DEPT 345" }, ], }, }, c=c) course_a = course_from_str("DEPT 123", clbid="0") course_b = course_from_str("DEPT 234", clbid="1") transcript = [course_a, course_b] solutions = list( area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) assert [[ x.course for x in s.solution.items if isinstance(x, CourseResult) ] for s in solutions] == [['DEPT 123', 'DEPT 234']] assert len(solutions) == 1 result = solutions[0].audit() assert result.result.count == 1 assert result.is_ok() is True assert result.is_waived() is False assert result.claims()[0].course.clbid == course_a.clbid
def x_test_overlaps(caplog: Any) -> None: # caplog.set_level(logging.DEBUG) area = AreaOfStudy.load(c=c, specification={ "result": {"all": [ {"requirement": "Core"}, {"requirement": "Electives"}, {"requirement": "Lessons"}, ]}, "requirements": { "Core": { "result": {"all": [ {"course": "MUSIC 212"}, {"course": "MUSIC 214"}, {"course": "MUSIC 237"}, {"course": "MUSIC 251"}, {"course": "MUSIC 261"}, {"course": "MUSIC 298"}, ]}, }, "Electives": { "result": { "from": "courses", "where": { "$and": [ {"subject": {"$eq": "MUSIC"}}, {"level": {"$in": [200, 300]}}, ], }, "all": [ {"assert": {"count(courses)": {"$gte": 8}}}, { "where": {"level": {"$eq": 300}}, "assert": {"count(courses)": {"$gte": 2}}, }, ], }, }, "Lessons": { "result": { "from": "courses", "where": {"subject": {"$eq": "MUSPF"}}, "all": [ {"assert": {"count(terms)": {"$gte": 6}}}, {"assert": {"sum(credits)": {"$gte": 2.5}}}, { "where": {"credits": {"$eq": 0.5}}, "assert": {"count(terms)": {"$gte": 4}}, }, ], }, }, }, }) transcript = [course_from_str(c) for c in [ "MUSIC 212", "MUSIC 214", "MUSIC 237", "MUSIC 251", "MUSIC 252", "MUSIC 298", "MUSIC 222", "MUSIC 224", "MUSIC 247", "MUSIC 261", "MUSIC 262", "MUSIC 299", "MUSIC 301", "MUSIC 302", "MUSPF 101", "MUSPF 102", "MUSPF 103", "MUSPF 104", "MUSPF 105", "MUSPF 106", ]] solutions = list(area.solutions(student=Student.load(dict(courses=transcript)), exceptions=[])) assert len(solutions) == 1 result = solutions[0].audit() assert result.is_ok() is True
def render(args: argparse.Namespace) -> None: stnum = args.stnum catalog = args.catalog code = args.code branch = args.branch base = args.base input_data: Optional[Dict] baseline_result: Optional[Dict] branch_result: Optional[Dict] = None with sqlite_connect(args.db, readonly=True) as conn: if branch == 'server': results = conn.execute( ''' SELECT d.input_data, d.result as output FROM server_data d WHERE d.stnum = :stnum AND d.catalog = :catalog AND d.code = :code ''', { 'catalog': catalog, 'code': code, 'stnum': stnum }) record = results.fetchone() assert record, {'catalog': catalog, 'code': code, 'stnum': stnum} input_data = json.loads(record['input_data']) baseline_result = json.loads(record['output']) elif branch == 'baseline': results = conn.execute( ''' SELECT d.input_data, b1.result as output FROM server_data d LEFT JOIN baseline b1 ON (b1.stnum, b1.catalog, b1.code) = (d.stnum, d.catalog, d.code) WHERE d.stnum = :stnum AND d.catalog = :catalog AND d.code = :code ''', { 'catalog': catalog, 'code': code, 'stnum': stnum }) record = results.fetchone() assert record, {'catalog': catalog, 'code': code, 'stnum': stnum} input_data = json.loads(record['input_data']) baseline_result = json.loads(record['output']) else: if base == 'baseline': results = conn.execute( ''' SELECT d.input_data, b1.result as baseline, b2.result as branch FROM server_data d LEFT JOIN baseline b1 ON (b1.stnum, b1.catalog, b1.code) = (d.stnum, d.catalog, d.code) LEFT JOIN branch b2 ON (b2.stnum, b2.catalog, b2.code) = (d.stnum, d.catalog, d.code) WHERE d.stnum = :stnum AND d.catalog = :catalog AND d.code = :code AND b2.branch = :branch ''', { 'catalog': catalog, 'code': code, 'stnum': stnum, 'branch': branch }) else: results = conn.execute( ''' SELECT d.input_data, b1.result as baseline, b2.result as branch FROM server_data d LEFT JOIN branch b1 ON (b1.stnum, b1.catalog, b1.code) = (d.stnum, d.catalog, d.code) LEFT JOIN branch b2 ON (b2.stnum, b2.catalog, b2.code) = (d.stnum, d.catalog, d.code) WHERE d.stnum = :stnum AND d.catalog = :catalog AND d.code = :code AND b1.branch = :base AND b2.branch = :branch ''', { 'catalog': catalog, 'code': code, 'stnum': stnum, 'branch': branch, 'base': base }) record = results.fetchone() assert record, f"could not find record matching catalog={catalog}, code={code}, stnum={stnum}, branch={branch}" input_data = json.loads(record['input_data']) baseline_result = json.loads(record['baseline']) branch_result = json.loads(record['branch']) assert input_data assert baseline_result student = Student.load(input_data) if not branch_result: print(render_result(student, baseline_result)) return if args.diff: baseline_result_lines = render_result(student, baseline_result).split('\n') branch_result_lines = render_result(student, branch_result).split('\n') if baseline_result_lines == branch_result_lines: print('no changes') return else: d = difflib.Differ() print('\n'.join( d.compare(baseline_result_lines, branch_result_lines))) return print('Baseline') print('========') print() print(render_result(student, baseline_result)) print() print() label = f'Branch: {branch}' print(label) print('=' * len(label)) print() print(render_result(student, branch_result))