source: server/lib/gutenbach/server/job.py @ 609a9b0

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

Add basic configuration file support

  • Property mode set to 100644
File size: 9.2 KB
Line 
1from . import errors
2from .player import Player
3from gutenbach.ipp import JobStates as States
4import logging
5import os
6import time
7
8# initialize logger
9logger = logging.getLogger(__name__)
10
11class GutenbachJob(object):
12
13    def __init__(self, job_id=None, creator=None, name=None,
14                 priority=None, document=None):
15        """Create an empty Gutenbach job.
16
17        """
18
19        self.player = None
20        self.document = None
21
22        self.id = job_id
23        self.creator = creator
24        self.name = name
25        self.priority = priority
26        self._why_done = None
27
28        if document is not None:
29            self.spool(document)
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        """State status codes; equivalent to the IPP job-state status
186        codes.
187       
188        State transitions are as follows:
189        HELD ---> PENDING ---> PROCESSING <--> STOPPED (aka paused)
190                     ^              |---> CANCELLED
191                     |              |---> ABORTED
192                     |              |---> COMPLETE ---|
193                     |--------------------------------|
194                     
195        """
196        if self.is_ready:
197            state = States.PENDING
198        elif self.is_playing and not self.is_paused:
199            state = States.PROCESSING
200        elif self.is_playing and self.is_paused:
201            state = States.STOPPED
202        elif self.is_completed:
203            state = States.COMPLETE
204        elif self.is_cancelled:
205            state = States.CANCELLED
206        elif self.is_aborted:
207            state = States.ABORTED
208        else:
209            state = States.HELD
210        return state
211
212    ######################################################################
213    ###                            Methods                             ###
214    ######################################################################
215
216    @staticmethod
217    def verify_document(document):
218        if not hasattr(document, "name"):
219            raise errors.InvalidDocument, "no name attribute"
220        if not hasattr(document, "read"):
221            raise errors.InvalidDocument, "no read attribute"
222        if not hasattr(document, "close"):
223            raise errors.InvalidDocument, "no close attribute"
224
225    def spool(self, document=None):
226        """Non-blocking spool.  Job must be valid, and the document
227        must be an open file handler.
228
229        Raises
230        ------
231        InvalidDocument
232            If the document is not valid.
233        InvalidJobStateException
234            If the job is not valid or it is already
235            spooled/ready/finished.
236
237        """
238
239        if not self.is_valid or self.state != States.HELD:
240            raise errors.InvalidJobStateException(self.state)
241        self.verify_document(document)
242        self.document = document.name
243        self.player = Player(document)
244        logger.debug("document for job %d is '%s'" % (self.id, self.document))
245
246    def play(self):
247        """Non-blocking play.  Job must be ready.
248
249        Raises
250        ------
251        InvalidJobStateException
252            If the job is not ready to be played.
253
254        """
255       
256        # make sure the job is waiting to be played and that it's
257        # valid
258        if not self.is_ready:
259            raise errors.InvalidJobStateException(self.state)
260       
261        # and set the state to processing if we're good to go
262        logger.info("playing job %s" % str(self))
263
264        def _completed():
265            logger.info("completed job %s" % str(self))
266            self._why_done = "completed"
267        self.player.callback = _completed
268        self.player.start()
269
270    def pause(self):
271        """Non-blocking pause.  Job must be playing.
272
273        Raises
274        ------
275        InvalidJobStateException
276            If the job is not playing.
277
278        """
279       
280        if not self.is_playing:
281            raise errors.InvalidJobStateException(self.state)
282        self.player.mplayer_pause()
283
284    def cancel(self):
285        """Non-blocking cancel. The job must not have previously
286        finished (i.e., cannot be aborted, cancelled, or completed).
287        This should be used to stop the job following an external
288        request.
289
290        Raises
291        ------
292        InvalidJobStateException
293            If the job has already finished.
294
295        """
296       
297        def _cancelled():
298            logger.info("cancelled job %s" % str(self))
299            self._why_done = "cancelled"
300
301        if self.is_playing:
302            self.player.callback = _cancelled
303            self.player.mplayer_stop()
304        elif self.is_done and not self._why_done == "cancelled":
305            raise errors.InvalidJobStateException(self.state)
306        else:
307            _cancelled()
308
309    def abort(self):
310        """Non-blocking abort. The job must not have previously
311        finished (i.e., cannot be aborted, cancelled, or completed).
312        This should be used to stop the job following internal errors.
313
314        Raises
315        ------
316        InvalidJobStateException
317            If the job has already finished.
318
319        """
320
321        def _aborted():
322            logger.info("aborted job %s" % str(self))
323            self._why_done = "aborted"
324
325        if self.is_playing:
326            self.player.callback = _aborted
327            self.player.mplayer_stop()
328        elif self.is_done and not self._why_done == "aborted":
329            raise errors.InvalidJobStateException(self.state)
330        else:
331            _aborted()
332
333    def restart(self):
334        # XXX: Todo
335        pass
Note: See TracBrowser for help on using the repository browser.