1d56ac0edeff0a2bcc962a82d3b564af60b67f59
[pygrader.git] / pygrader / model / course.py
1 # Copyright
2
3 from .. import LOG as _LOG
4
5
6 class Course (object):
7     def __init__(self, assignments=None, people=None, grades=None):
8         if assignments is None:
9             assignments = []
10         self.assignments = sorted(assignments)
11         if people is None:
12             people = []
13         self.people = sorted(people)
14         if grades is None:
15             grades = []
16         self.grades = sorted(grades)
17
18     def assignment(self, name):
19         for assignment in self.assignments:
20             if assignment.name == name:
21                 return assignment
22         raise ValueError(name)
23
24     def active_assignments(self):
25         return sorted(set(grade.assignment for grade in self.grades))
26
27     def active_groups(self):
28         groups = set()
29         for person in self.people:
30             groups.update(person.groups)
31         return sorted(groups)
32
33     def find_people(self, name=None, email=None, group=None):
34         """Yield ``Person``\s that match ``name``, ``email``, and ``group``
35
36         The value of ``None`` matches any person.
37
38         >>> from pygrade.model.person import Person
39         >>> c = Course(people=[
40         ...     Person(name='Bilbo Baggins',
41         ...            emails=['bb@shire.org', 'bb@greyhavens.net'],
42         ...            aliases=['Billy'],
43         ...            groups=['students', 'assistants']),
44         ...     Person(name='Frodo Baggins',
45         ...            emails=['fb@shire.org'],
46         ...            groups=['students']),
47         ...     ])
48         >>> for person in c.find_people(name='Bilbo Baggins'):
49         ...     print(person)
50         <Person Bilbo Baggins>
51         >>> for person in c.find_people(name='Billy'):
52         ...     print(person)
53         <Person Bilbo Baggins>
54         >>> for person in c.find_people(email='bb@greyhavens.net'):
55         ...     print(person)
56         <Person Bilbo Baggins>
57         >>> for person in c.find_people(group='assistants'):
58         ...     print(person)
59         <Person Bilbo Baggins>
60         >>> for person in c.find_people(group='students'):
61         ...     print(person)
62         <Person Bilbo Baggins>
63         <Person Frodo Baggins>
64         """
65         for person in self.people:
66             name_match = (person.name == name or
67                           (person.aliases and name in person.aliases))
68             email_match = email in person.emails
69             group_match = group in person.groups
70             matched = True
71             for (key,kmatched) in [(name, name_match),
72                                    (email, email_match),
73                                    (group, group_match),
74                                    ]:
75                 if key is not None and not kmatched:
76                     matched = False
77                     break
78             if matched:
79                 yield person
80
81     def person(self, **kwargs):
82         people = list(self.find_people(**kwargs))
83         assert len(people) == 1, '{} -> {}'.format(kwargs, people)
84         return people[0]
85
86     def grade(self, student, assignment):
87         """Return the ``Grade`` that matches ``Student`` and ``Assignment``
88
89         >>> from pygrade.model.assignment import Assignment
90         >>> from pygrade.model.grade import Grade
91         >>> from pygrade.model.person import Person
92         >>> p = Person(name='Bilbo Baggins')
93         >>> a = Assignment(name='Exam 1')
94         >>> g = Grade(student=p, assignment=a, points=10)
95         >>> c = Course(assignments=[a], people=[p], grades=[g])
96         >>> print(c.grade(student=p, assignment=a))
97         <Grade Bilbo Baggins:Exam 1>
98         """
99         for grade in self.grades:
100             if grade.student == student and grade.assignment == assignment:
101                 return grade
102         raise ValueError((student, assignment))
103
104     def total(self, student):
105         total = 0
106         for assignment in self.assignments:
107             try:
108                 grade = self.grade(student=student, assignment=assignment)
109             except ValueError:
110                 continue
111             total += float(grade.points)/assignment.points * assignment.weight
112         return total