~ubuntu-branches/ubuntu/natty/schooltool.gradebook/natty

« back to all changes in this revision

Viewing changes to src/schooltool/requirement/evaluation.py

  • Committer: Bazaar Package Importer
  • Author(s): Gediminas Paulauskas
  • Date: 2011-02-24 16:53:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110224165353-hi69ckyal3b8dyns
Tags: upstream-0.9.0~a1
ImportĀ upstreamĀ versionĀ 0.9.0~a1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# This program is free software; you can redistribute it and/or modify
 
3
# it under the terms of the GNU General Public License as published by
 
4
# the Free Software Foundation; either version 2 of the License, or
 
5
# (at your option) any later version.
 
6
#
 
7
# This program is distributed in the hope that it will be useful,
 
8
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
# GNU General Public License for more details.
 
11
#
 
12
# You should have received a copy of the GNU General Public License
 
13
# along with this program; if not, write to the Free Software
 
14
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
#
 
16
"""
 
17
Level-related Tests
 
18
 
 
19
$Id$
 
20
"""
 
21
 
 
22
__docformat__ = 'restructuredtext'
 
23
 
 
24
import datetime
 
25
import persistent
 
26
from BTrees.OOBTree import OOBTree
 
27
 
 
28
import zope.event
 
29
from zope.interface import implements
 
30
from zope.component import adapts
 
31
from zope.annotation.interfaces import IAnnotations
 
32
from zope.container.contained import Contained, containedEvent
 
33
from zope.lifecycleevent import ObjectRemovedEvent
 
34
from zope.location import location
 
35
from zope.keyreference.interfaces import IKeyReference
 
36
from zope.traversing.api import getParent, getName
 
37
 
 
38
from schooltool.requirement import interfaces
 
39
 
 
40
 
 
41
EVALUATIONS_KEY = "schooltool.evaluations"
 
42
 
 
43
 
 
44
def getRequirementList(req, recurse=True):
 
45
    result = []
 
46
    for value in req.values(): # loop through your children
 
47
        if recurse:
 
48
            result += getRequirementList(value) # append their children...
 
49
        else:
 
50
            result.append(value) # just append the child
 
51
    result.append(req) # append the object itself
 
52
    return result
 
53
 
 
54
 
 
55
class Evaluations(persistent.Persistent, Contained):
 
56
    """Evaluations mapping.
 
57
 
 
58
    This particular implementation uses the ``zope.keyreference`` package
 
59
    to generate the keys of the requirements. Any key that is passed in could
 
60
    be the requirement or the ``IKeyReference`` of the requirement. This
 
61
    implementation will always convert the key to provide ``IKeyReference``
 
62
    before treating it as a true key.
 
63
 
 
64
    Another feature of this implementation is that if you set an evaluation
 
65
    for a requirement that has already an evaluation, then the old evaluation
 
66
    is simply overridden. The ``IContainer`` interface would raise a duplicate
 
67
    name error.
 
68
    """
 
69
    implements(interfaces.IEvaluations)
 
70
 
 
71
    def __init__(self, items=None):
 
72
        super(Evaluations, self).__init__()
 
73
        self._btree = OOBTree()
 
74
        for name, value in items or []:
 
75
            self[name] = value
 
76
 
 
77
    def __getitem__(self, key):
 
78
        """See zope.interface.common.mapping.IItemMapping"""
 
79
        return self._btree[IKeyReference(key)]
 
80
 
 
81
    def __delitem__(self, key):
 
82
        """See zope.interface.common.mapping.IWriteMapping"""
 
83
        value = self[key]
 
84
        del self._btree[IKeyReference(key)]
 
85
        event = ObjectRemovedEvent(value, self)
 
86
        zope.event.notify(event)
 
87
 
 
88
    def __setitem__(self, key, value):
 
89
        """See zope.interface.common.mapping.IWriteMapping"""
 
90
        self._btree[IKeyReference(key)] = value
 
91
        value, event = containedEvent(value, self)
 
92
        zope.event.notify(event)
 
93
 
 
94
    def get(self, key, default=None):
 
95
        """See zope.interface.common.mapping.IReadMapping"""
 
96
        try:
 
97
            return self[key]
 
98
        except KeyError:
 
99
            return default
 
100
 
 
101
    def __contains__(self, key):
 
102
        """See zope.interface.common.mapping.IReadMapping"""
 
103
        return IKeyReference(key) in self._btree
 
104
 
 
105
    def keys(self):
 
106
        """See zope.interface.common.mapping.IEnumerableMapping"""
 
107
        # For now I decided to return the activities (as I think it is more
 
108
        # natural), though they are not the true keys as we know
 
109
        return [key() for key in self._btree.keys()]
 
110
 
 
111
    def __iter__(self):
 
112
        """See zope.interface.common.mapping.IEnumerableMapping"""
 
113
        return iter(self.keys())
 
114
 
 
115
    def values(self):
 
116
        """See zope.interface.common.mapping.IEnumerableMapping"""
 
117
        return self._btree.values()
 
118
 
 
119
    def items(self):
 
120
        """See zope.interface.common.mapping.IEnumerableMapping"""
 
121
        return [(key(), value) for key, value in self._btree.items()]
 
122
 
 
123
    def __len__(self):
 
124
        """See zope.interface.common.mapping.IEnumerableMapping"""
 
125
        return len(self._btree)
 
126
 
 
127
    def addEvaluation(self, evaluation):
 
128
        """See interfaces.IEvaluations"""
 
129
        self[evaluation.requirement] = evaluation
 
130
 
 
131
    def getEvaluationsForRequirement(self, req, recurse=True):
 
132
        """See interfaces.IEvaluations"""
 
133
        requirements = getRequirementList(req, recurse)
 
134
        result = [(name, ev)
 
135
                  for name, ev in self.items()
 
136
                  if ev.requirement in requirements]
 
137
        result = Evaluations(result)
 
138
        location.locate(result, getParent(self), getName(self))
 
139
        return result
 
140
 
 
141
    def getEvaluationsOfEvaluator(self, evaluator):
 
142
        """See interfaces.IEvaluations"""
 
143
        result = [(name, ev)
 
144
                  for name, ev in self.items()
 
145
                  if ev.evaluator == evaluator]
 
146
        result = Evaluations(result)
 
147
        location.locate(result, getParent(self), getName(self))
 
148
        return result
 
149
 
 
150
    def __repr__(self):
 
151
        try:
 
152
            parent = getParent(self)
 
153
        except TypeError:
 
154
            parent = None
 
155
        return '<%s for %r>' % (self.__class__.__name__, parent)
 
156
 
 
157
 
 
158
class Evaluation(Contained):
 
159
 
 
160
    implements(interfaces.IEvaluation)
 
161
 
 
162
    def __init__(self, requirement, scoreSystem, value, evaluator):
 
163
        self._value = None
 
164
        self.requirement = requirement
 
165
        self.scoreSystem = scoreSystem
 
166
        self.value = value
 
167
        self.evaluator = evaluator
 
168
 
 
169
    @apply
 
170
    def value():
 
171
        def get(self):
 
172
            return self._value
 
173
 
 
174
        def set(self, value):
 
175
            if not self.scoreSystem.isValidScore(value):
 
176
                raise ValueError('%r is not a valid score.' %value)
 
177
            self._value = value
 
178
            # XXX mg: since it is a very bad idea to mix datetimes with tzinfo
 
179
            # and datetimes without tzinfo, I suggest using datetimes with
 
180
            # tzinfo everywhere.  Most of SchoolTool follows this convention,
 
181
            # (with the painful exception of schooltool.timetable).
 
182
            self.time = datetime.datetime.utcnow()
 
183
 
 
184
        return property(get, set)
 
185
 
 
186
    @property
 
187
    def evaluatee(self):
 
188
        try:
 
189
            return getParent(getParent(self))
 
190
        except TypeError:
 
191
            raise ValueError('Evaluation is not yet assigned to a evaluatee')
 
192
 
 
193
    def __repr__(self):
 
194
        return '<%s for %r, value=%r>' % (self.__class__.__name__,
 
195
                                          self.requirement, self.value)
 
196
 
 
197
 
 
198
class AbstractQueryAdapter(object):
 
199
 
 
200
    adapts(interfaces.IEvaluations)
 
201
    implements(interfaces.IEvaluationsQuery)
 
202
 
 
203
    def __init__(self, context):
 
204
        self.context = context
 
205
 
 
206
    def _query(self, *args, **kwargs):
 
207
        raise NotImplemented
 
208
 
 
209
    def __call__(self, *args, **kwargs):
 
210
        """See interfaces.IEvaluationsQuery"""
 
211
        result = Evaluations(self._query(*args, **kwargs))
 
212
        location.locate(
 
213
            result, getParent(self.context), getName(self.context))
 
214
        return result
 
215
 
 
216
 
 
217
def getEvaluations(context):
 
218
    """Adapt an ``IHaveEvaluations`` object to ``IEvaluations``."""
 
219
    annotations = IAnnotations(context)
 
220
    try:
 
221
        return annotations[EVALUATIONS_KEY]
 
222
    except KeyError:
 
223
        evaluations = Evaluations()
 
224
        annotations[EVALUATIONS_KEY] = evaluations
 
225
        location.locate(evaluations, zope.proxy.removeAllProxies(context),
 
226
                        '++evaluations++')
 
227
        return evaluations
 
228
# Convention to make adapter introspectable
 
229
getEvaluations.factory = Evaluations
 
230