source: server/lib/gutenbach/server/job.py @ 951ab1b

no-cups
Last change on this file since 951ab1b was 951ab1b, checked in by Jessica B. Hamrick <jhamrick@…>, 12 years ago

Add support for verifying documents in job.py

  • Property mode set to 100644
File size: 9.0 KB
Line 
1from . import errors
2from .player import Player
3from gutenbach.ipp import JobStates as States
4import logging
5import os
6
7# initialize logger
8logger = logging.getLogger(__name__)
9
10class GutenbachJob(object):
11
12    def __init__(self, job_id=None, creator=None, name=None,
13                 priority=None, document=None):
14        """Create an empty Gutenbach job.
15
16        """
17
18        self.player = None
19        self.document = None
20
21        self.id = job_id
22        self.creator = creator
23        self.name = name
24        self.priority = priority
25
26        if document is not None:
27            self.spool(document)
28
29        self._why_done = None
30
31    def __repr__(self):
32        return str(self)
33
34    def __str__(self):
35        return "<Job %d '%s'>" % (self.id, self.name)
36
37    def __cmp__(self, other):
38        return cmp(self.priority, other.priority)
39
40    ######################################################################
41    ###                          Properties                            ###
42    ######################################################################
43
44    @property
45    def id(self):
46        """Unique job identifier.  Should be a positive integer,
47        except when unassigned, when it defaults to -1.
48       
49        """
50        return self._id
51    @id.setter
52    def id(self, val):
53        try:
54            self._id = max(int(val), -1)
55        except:
56            self._id = -1
57
58    @property
59    def priority(self):
60        return self._priority
61    @priority.setter
62    def priority(self, val):
63        try:
64            self._priority = max(int(val), 1)
65        except:
66            self._priority = 1
67
68    @property
69    def creator(self):
70        """The user who created the job; analogous to the IPP
71        requesting-user-name.
72
73        """
74        return self._creator
75    @creator.setter
76    def creator(self, val):
77        if val is None:
78            self._creator = ""
79        else:
80            self._creator = str(val)
81
82    @property
83    def name(self):
84        """The job's name.
85
86        """
87        return self._name
88    @name.setter
89    def name(self, val):
90        if val is None:
91            self._name = ""
92        else:
93            self._name = str(val)
94
95    @property
96    def size(self):
97        """The size of the job in bytes.
98
99        """
100        try:
101            size = os.path.getsize(self.document)
102        except:
103            size = 0
104        return size
105
106    ######################################################################
107    ###                            State                               ###
108    ######################################################################
109
110    @property
111    def is_valid(self):
112        """Whether the job is ready to be manipulated (spooled,
113        played, etc).  Note that playing the job still requires it to
114        be spooled first.
115
116        """
117        return self.id > 0 and \
118               self.priority > 0
119
120    @property
121    def is_ready(self):
122        """Whether the job is ready to be played.
123
124        """
125        return self.is_valid and \
126               self.player is not None and \
127               not self.player.is_playing and \
128               not self._why_done == "cancelled" and \
129               not self._why_done == "aborted"
130
131    @property
132    def is_playing(self):
133        """Whether the job is currently playing (regardless of whether
134        it's paused).
135
136        """
137        return self.is_valid and \
138               self.player is not None and \
139               self.player.is_playing
140
141    @property
142    def is_paused(self):
143        """Whether the job is currently paused.
144
145        """
146        return self.is_valid and \
147               self.player is not None and \
148               self.player.is_paused       
149
150    @property
151    def is_done(self):
152        """Whether the job is done playing, regardless of whether it
153        completed successfully or not.
154
155        """
156        return (self.is_valid and \
157                self.player is not None and \
158                self.player.is_done) or \
159                (self._why_done == "cancelled" or \
160                 self._why_done == "aborted")
161
162    @property
163    def is_completed(self):
164        """Whether the job completed successfully.
165
166        """
167        return self.is_done and self._why_done == "completed"
168
169    @property
170    def is_cancelled(self):
171        """Whether the job was cancelled.
172
173        """
174        return self.is_done and self._why_done == "cancelled"
175
176    @property
177    def is_aborted(self):
178        """Whether the job was aborted.
179
180        """
181        return self.is_done and self._why_done == "aborted"
182
183    @property
184    def state(self):
185        """
186        State transitions are as follows:
187HELD ---> PENDING ---> PROCESSING <--> STOPPED (aka paused)
188             ^              |---> CANCELLED
189             |              |---> ABORTED
190             |              |---> COMPLETE ---|
191             |--------------------------------|
192        """
193        if self.is_ready:
194            state = States.PENDING
195        elif self.is_playing and not self.is_paused:
196            state = States.PROCESSING
197        elif self.is_playing and self.is_paused:
198            state = States.STOPPED
199        elif self.is_completed:
200            state = States.COMPLETE
201        elif self.is_cancelled:
202            state = States.CANCELLED
203        elif self.is_aborted:
204            state = States.ABORTED
205        else:
206            state = States.HELD
207        return state
208
209    ######################################################################
210    ###                            Methods                             ###
211    ######################################################################
212
213    @staticmethod
214    def verify_document(document):
215        if not hasattr(document, "name"):
216            raise errors.InvalidDocument, "no name attribute"
217        if not hasattr(document, "read"):
218            raise errors.InvalidDocument, "no read attribute"
219        if not hasattr(document, "close"):
220            raise errors.InvalidDocument, "no close attribute"
221
222    def spool(self, document=None):
223        """Non-blocking spool.  Job must be valid, and the document
224        must be an open file handler.
225
226        Raises
227        ------
228        InvalidDocument
229            If the document is not valid.
230        InvalidJobStateException
231            If the job is not valid or it is already
232            spooled/ready/finished.
233
234        """
235
236        if not self.is_valid or self.state != States.HELD:
237            raise errors.InvalidJobStateException(self.state)
238        self.verify_document(document)
239        self.document = document.name
240        self.player = Player(document)
241        logger.debug("document for job %d is '%s'" % (self.id, self.document))
242
243    def play(self):
244        """Non-blocking play.  Job must be ready.
245
246        Raises
247        ------
248        InvalidJobStateException
249            If the job is not ready to be played.
250
251        """
252       
253        # make sure the job is waiting to be played and that it's
254        # valid
255        if not self.is_ready:
256            raise errors.InvalidJobStateException(self.state)
257       
258        # and set the state to processing if we're good to go
259        logger.info("playing job %s" % str(self))
260
261        def _completed():
262            logger.info("completed job %s" % str(self))
263            self._why_done = "completed"
264        self.player.callback = _completed
265        self.player.start()
266
267    def pause(self):
268        """Non-blocking pause.  Job must be playing.
269
270        Raises
271        ------
272        InvalidJobStateException
273            If the job is not playing.
274
275        """
276       
277        if not self.is_playing:
278            raise errors.InvalidJobStateException(self.state)
279        self.player.mplayer_pause()
280
281    def cancel(self):
282        """Non-blocking cancel. The job must not have previously
283        finished (i.e., cannot be aborted, cancelled, or completed).
284        This should be used to stop the job following an external
285        request.
286
287        Raises
288        ------
289        InvalidJobStateException
290            If the job has already finished.
291
292        """
293       
294        def _cancelled():
295            logger.info("cancelled job %s" % str(self))
296            self._why_done = "cancelled"
297
298        if self.is_playing:
299            self.player.callback = _cancelled
300            self.player.mplayer_stop()
301        elif self.is_done and not self._why_done == "cancelled":
302            raise errors.InvalidJobStateException(self.state)
303        else:
304            _cancelled()
305
306    def abort(self):
307        """Non-blocking abort. The job must not have previously
308        finished (i.e., cannot be aborted, cancelled, or completed).
309        This should be used to stop the job following internal errors.
310
311        Raises
312        ------
313        InvalidJobStateException
314            If the job has already finished.
315
316        """
317
318        def _aborted():
319            logger.info("aborted job %s" % str(self))
320            self._why_done = "aborted"
321
322        if self.is_playing:
323            self.player.callback = _aborted
324            self.player.mplayer_stop()
325        elif self.is_done and not self._why_done == "aborted":
326            raise errors.InvalidJobStateException(self.state)
327        else:
328            _aborted()
329
330
Note: See TracBrowser for help on using the repository browser.