559be78aa4fd4caaa3da5a69435a21baeeb413e3
[pygrader.git] / pygrader / model / course.py
1 # Copyright (C) 2012 W. Trevor King <wking@drexel.edu>
2 #
3 # This file is part of pygrader.
4 #
5 # pygrader is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
8 # version.
9 #
10 # pygrader is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # pygrader.  If not, see <http://www.gnu.org/licenses/>.
16
17 from .. import LOG as _LOG
18
19
20 class Course (object):
21     def __init__(self, name=None, assignments=None, people=None, grades=None,
22                  robot=None):
23         self.name = name
24         if assignments is None:
25             assignments = []
26         self.assignments = sorted(assignments)
27         if people is None:
28             people = []
29         self.people = sorted(people)
30         if grades is None:
31             grades = []
32         self.grades = sorted(grades)
33         self.robot = robot
34
35     def assignment(self, name):
36         for assignment in self.assignments:
37             if assignment.name == name:
38                 return assignment
39         raise ValueError(name)
40
41     def active_assignments(self):
42         return sorted(set(grade.assignment for grade in self.grades))
43
44     def active_groups(self):
45         groups = set()
46         for person in self.people:
47             groups.update(person.groups)
48         return sorted(groups)
49
50     def find_people(self, name=None, email=None, group=None):
51         """Yield ``Person``\s that match ``name``, ``email``, and ``group``
52
53         The value of ``None`` matches any person.
54
55         >>> from pygrader.model.person import Person
56         >>> c = Course(people=[
57         ...     Person(name='Bilbo Baggins',
58         ...            emails=['bb@shire.org', 'bb@greyhavens.net'],
59         ...            aliases=['Billy'],
60         ...            groups=['students', 'assistants']),
61         ...     Person(name='Frodo Baggins',
62         ...            emails=['fb@shire.org'],
63         ...            groups=['students']),
64         ...     ])
65         >>> for person in c.find_people(name='Bilbo Baggins'):
66         ...     print(person)
67         <Person Bilbo Baggins>
68         >>> for person in c.find_people(name='Billy'):
69         ...     print(person)
70         <Person Bilbo Baggins>
71         >>> for person in c.find_people(email='bb@greyhavens.net'):
72         ...     print(person)
73         <Person Bilbo Baggins>
74         >>> for person in c.find_people(group='assistants'):
75         ...     print(person)
76         <Person Bilbo Baggins>
77         >>> for person in c.find_people(group='students'):
78         ...     print(person)
79         <Person Bilbo Baggins>
80         <Person Frodo Baggins>
81         """
82         for person in self.people:
83             name_match = (person.name == name or
84                           (person.aliases and name in person.aliases))
85             email_match = email in person.emails
86             group_match = group in person.groups
87             matched = True
88             for (key,kmatched) in [(name, name_match),
89                                    (email, email_match),
90                                    (group, group_match),
91                                    ]:
92                 if key is not None and not kmatched:
93                     matched = False
94                     break
95             if matched:
96                 yield person
97
98     def person(self, **kwargs):
99         people = list(self.find_people(**kwargs))
100         assert len(people) == 1, '{} -> {}'.format(kwargs, people)
101         return people[0]
102
103     def grade(self, student, assignment):
104         """Return the ``Grade`` that matches ``Student`` and ``Assignment``
105
106         >>> from pygrader.model.assignment import Assignment
107         >>> from pygrader.model.grade import Grade
108         >>> from pygrader.model.person import Person
109         >>> p = Person(name='Bilbo Baggins')
110         >>> a = Assignment(name='Exam 1')
111         >>> g = Grade(student=p, assignment=a, points=10)
112         >>> c = Course(assignments=[a], people=[p], grades=[g])
113         >>> print(c.grade(student=p, assignment=a))
114         <Grade Bilbo Baggins:Exam 1>
115         """
116         for grade in self.grades:
117             if grade.student == student and grade.assignment == assignment:
118                 return grade
119         raise ValueError((student, assignment))
120
121     def total(self, student):
122         total = 0
123         for assignment in self.assignments:
124             try:
125                 grade = self.grade(student=student, assignment=assignment)
126             except ValueError:
127                 continue
128             total += float(grade.points)/assignment.points * assignment.weight
129         return total