source: server/lib/gutenbach/server/printer.py @ c500bc2

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

Implement promote_job function in printer.py

  • Property mode set to 100644
File size: 12.2 KB
RevLine 
[33ea505]1from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
2from .job import GutenbachJob
[b01b6d1]3from gutenbach.ipp import PrinterStates as States
[b2e077a]4import gutenbach.ipp as ipp
5import logging
6import time
[eee389a]7import threading
8import heapq
9import traceback
10import sys
11
[d04a689]12
13# initialize logger
14logger = logging.getLogger(__name__)
[776a659]15
[eee389a]16class GutenbachPrinter(threading.Thread):
[b2e077a]17
[1a63bf7]18    # for IPP
[33ea505]19    printer_attributes = [
[b2e077a]20        "printer-uri-supported",
21        "uri-authentication-supported",
22        "uri-security-supported",
23        "printer-name",
24        "printer-state",
25        "printer-state-reasons",
26        "ipp-versions-supported",
27        "operations-supported",
28        "charset-configured",
29        "charset-supported",
30        "natural-language-configured",
31        "generated-natural-language-supported",
32        "document-format-default",
33        "document-format-supported",
34        "printer-is-accepting-jobs",
35        "queued-job-count",
36        "pdl-override-supported",
37        "printer-up-time",
[f6e2532]38        "compression-supported",
39        "multiple-operation-time-out",
40        "multiple-document-jobs-supported",
[1a63bf7]41    ]
[b2e077a]42
[33ea505]43    job_attributes = [
44        "job-id",
45        "job-name",
46        "job-originating-user-name",
47        "job-k-octets",
48        "job-state",
49        "job-printer-uri"
50    ]
51
[f6e2532]52    operations = [
53        "print-job",
[33ea505]54        "validate-job",
[f6e2532]55        "get-jobs",
[33ea505]56        "print-uri",
57        "create-job",
58        "pause-printer",
59        "resume-printer",
60        "get-printer-attributes",
61        "set-printer-attributes",
62        "cancel-job",
63        "send-document",
64        "send-uri",
65        "get-job-attributes",
66        "set-job-attributes",
67        "restart-job",
68        "promote-job"
[f6e2532]69    ]
70       
[609a9b0]71    def __init__(self, name, config, *args, **kwargs):
[eee389a]72
[d21198f]73        super(GutenbachPrinter, self).__init__(*args, **kwargs)
74       
75        self.name = name
[609a9b0]76        self.config = config
[d21198f]77        self.time_created = int(time.time())
[b2e077a]78
[d21198f]79        self.finished_jobs = []
80        self.pending_jobs = []
81        self.current_job = None
82        self.jobs = {}
[b2e077a]83
[d21198f]84        self.lock = threading.RLock()
85        self.running = False
86        self.paused = False
[776a659]87
[d21198f]88        # CUPS ignores jobs with id 0, so we have to start at 1
89        self._next_job_id = 1
[b01b6d1]90
91    def __repr__(self):
92        return str(self)
93
94    def __str__(self):
95        return "<Printer '%s'>" % self.name
96
[33ea505]97    def run(self):
98        self.running = True
99        while self.running:
100            with self.lock:
101                try:
102                    if self.current_job is None:
103                        self.start_job()
[345c476]104                    elif self.current_job.is_done:
[33ea505]105                        self.complete_job()
106                except:
107                    logger.fatal(traceback.format_exc())
108                    sys.exit(1)
109            time.sleep(0.1)
110
[b01b6d1]111    ######################################################################
112    ###                          Properties                            ###
113    ######################################################################
114
115    @property
116    def uris(self):
117        uris = ["ipp://localhost:8000/printers/" + self.name,
118                "ipp://localhost/printers/" + self.name]
119        return uris
120   
121    @property
122    def uri(self):
123        return self.uris[0]
124
125    @property
[eee389a]126    def state(self):
127        with self.lock:
128            if self.current_job is not None:
129                val = States.PROCESSING
130            elif len(self.pending_jobs) == 0:
131                val = States.IDLE
132            else:
133                val = States.STOPPED
134        return val
135
136    @property
137    def active_jobs(self):
138        with self.lock:
139            jobs = self.pending_jobs[:]
140            if self.current_job is not None:
141                jobs.insert(0, self.current_job.id)
142        return jobs
[b01b6d1]143
144    ######################################################################
145    ###                            Methods                             ###
146    ######################################################################
147
[eee389a]148    def start_job(self):
149        with self.lock:
150            if self.current_job is None:
151                try:
152                    job_id = heapq.heappop(self.pending_jobs)
153                    self.current_job = self.get_job(job_id)
154                    self.current_job.play()
155                except IndexError:
[d21198f]156                    self.current_job = None
[eee389a]157                except InvalidJobStateException:
158                    heapq.heappush(self.pending_jobs, self.current_job.id)
159                    self.current_job = None
160                   
161    def complete_job(self):
162        with self.lock:
163            if self.current_job is None:
164                return
165
166            try:
[345c476]167                if not self.current_job.is_done:
[eee389a]168                    self.current_job.stop()
169            finally:
[190bfb4]170                self.finished_jobs.append(self.current_job.id)
[eee389a]171                self.current_job = None
[1a63bf7]172
[eee389a]173    def get_job(self, job_id):
174        with self.lock:
175            if job_id not in self.jobs:
176                raise InvalidJobException(job_id)
177            job = self.jobs[job_id]
178        return job
[b01b6d1]179
180    ######################################################################
181    ###                        IPP Attributes                          ###
182    ######################################################################
[1a63bf7]183
[b2e077a]184    @property
185    def printer_uri_supported(self):
[793432f]186        return ipp.PrinterUriSupported(self.uri)
[1a63bf7]187
[b2e077a]188    @property
189    def uri_authentication_supported(self):
[793432f]190        return ipp.UriAuthenticationSupported("none")
[b2e077a]191
192    @property
193    def uri_security_supported(self):
[793432f]194        return ipp.UriSecuritySupported("none")
[b2e077a]195
196    @property
197    def printer_name(self):
[793432f]198        return ipp.PrinterName(self.name)
[1a63bf7]199
[b2e077a]200    @property
201    def printer_state(self):
[b01b6d1]202        return ipp.PrinterState(self.state)
[1a63bf7]203
[b2e077a]204    @property
205    def printer_state_reasons(self):
[793432f]206        return ipp.PrinterStateReasons("none")
[b2e077a]207
208    @property
209    def ipp_versions_supported(self):
[609a9b0]210        return ipp.IppVersionsSupported(*self.config['ipp-versions'])
[1a63bf7]211
[f6e2532]212    # XXX: We should query ourself for the supported operations
[b2e077a]213    @property
214    def operations_supported(self):
[793432f]215        return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS)
[b2e077a]216
217    @property
218    def charset_configured(self):
[793432f]219        return ipp.CharsetConfigured("utf-8")
[b2e077a]220
221    @property
222    def charset_supported(self):
[793432f]223        return ipp.CharsetSupported("utf-8")
[b2e077a]224
225    @property
226    def natural_language_configured(self):
[793432f]227        return ipp.NaturalLanguageConfigured("en-us")
[b2e077a]228
229    @property
230    def generated_natural_language_supported(self):
[793432f]231        return ipp.GeneratedNaturalLanguageSupported("en-us")
[b2e077a]232
233    @property
234    def document_format_default(self):
[793432f]235        return ipp.DocumentFormatDefault("application/octet-stream")
[b2e077a]236
237    @property
238    def document_format_supported(self):
[793432f]239        return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3")
[b2e077a]240
241    @property
242    def printer_is_accepting_jobs(self):
[793432f]243        return ipp.PrinterIsAcceptingJobs(True)
[b2e077a]244
245    @property
246    def queued_job_count(self):
[793432f]247        return ipp.QueuedJobCount(len(self.active_jobs))
[b2e077a]248
249    @property
250    def pdl_override_supported(self):
[793432f]251        return ipp.PdlOverrideSupported("not-attempted")
[b2e077a]252
253    @property
254    def printer_up_time(self):
[793432f]255        return ipp.PrinterUpTime(int(time.time()) - self.time_created)
[b2e077a]256
257    @property
258    def compression_supported(self):
[793432f]259        return ipp.CompressionSupported("none")
[b2e077a]260
[f6e2532]261    @property
262    def multiple_operation_time_out(self):
[793432f]263        return ipp.MultipleOperationTimeOut(240)
[f6e2532]264
265    @property
266    def multiple_document_jobs_supported(self):
[793432f]267        return ipp.MultipleDocumentJobsSupported(False)
[f6e2532]268
[33ea505]269    ######################################################################
270    ###                      Job IPP Attributes                        ###
271    ######################################################################
272
273    def job_id(self, job_id):
274        job = self.get_job(job_id)
275        return ipp.JobId(job.id)
276
277    def job_name(self, job_id):
278        job = self.get_job(job_id)
279        return ipp.JobName(job.name)
280
281    def job_originating_user_name(self, job_id):
282        job = self.get_job(job_id)
283        return ipp.JobOriginatingUserName(job.creator)
284
285    def job_k_octets(self, job_id):
286        job = self.get_job(job_id)
287        return ipp.JobKOctets(job.size)
288
289    def job_state(self, job_id):
290        job = self.get_job(job_id)
291        return ipp.JobState(job.state)
292
293    def job_printer_uri(self, job_id):
294        job = self.get_job(job_id)
295        return ipp.JobPrinterUri(self.uri)
[ee8e6d0]296
[b01b6d1]297    ######################################################################
298    ###                        IPP Operations                          ###
299    ######################################################################
300
301    def print_job(self):
302        pass
303
304    def validate_job(self):
305        pass
306
[33ea505]307    def get_jobs(self, requesting_user_name=None, which_jobs=None,
308                 requested_attributes=None):
309       
[b01b6d1]310        # Filter by the which-jobs attribute
311        if which_jobs is None:
[34a4e5d]312            which_jobs = "not-completed"
313
314        if which_jobs == "completed":
[b01b6d1]315            jobs = [self.jobs[job_id] for job_id in self.finished_jobs]
316        elif which_jobs == "not-completed":
317            jobs = [self.jobs[job_id] for job_id in self.active_jobs]
[ee8e6d0]318        else:
[b01b6d1]319            raise ipp.errors.ClientErrorAttributes(
320                which_jobs, ipp.WhichJobs(which_jobs))
[b2e077a]321
[b01b6d1]322        # Filter by username
323        if requesting_user_name is None:
324            user_jobs = jobs
325        else:
326            user_jobs = [job for job in jobs if job.creator == requesting_user_name]
[33ea505]327
328        # Get the attributes of each job
329        job_attrs = [self.get_job_attributes(
330            job.id, requested_attributes=requested_attributes) for job in user_jobs]
[ee8e6d0]331       
[33ea505]332        return job_attrs
[ee8e6d0]333
[b01b6d1]334    def print_uri(self):
335        pass
336
[190bfb4]337    def create_job(self, requesting_user_name=None, job_name=None, job_k_octets=None):
[eee389a]338        job_id = self._next_job_id
339        self._next_job_id += 1
[ee8e6d0]340       
[33ea505]341        job = GutenbachJob(
342            job_id,
343            creator=requesting_user_name,
344            name=job_name)
[b01b6d1]345       
[ee8e6d0]346        self.jobs[job_id] = job
[eee389a]347        self.pending_jobs.append(job_id)
[b01b6d1]348       
[33ea505]349        return job_id
[776a659]350
[b01b6d1]351    def pause_printer(self):
[ee8e6d0]352        pass
[776a659]353
[b01b6d1]354    def resume_printer(self):
355        pass
[776a659]356
[b01b6d1]357    def get_printer_attributes(self, requested_attributes=None):
358        if requested_attributes is None:
[33ea505]359            requested = self.printer_attributes
[e58af05]360        else:
[33ea505]361            requested = [a for a in self.printer_attributes \
362                         if a in requested_attributes]
[b2e077a]363
[b01b6d1]364        _attributes = [attr.replace("-", "_") for attr in requested]
365        attributes = [getattr(self, attr) for attr in _attributes]
366        return attributes
[776a659]367
[b01b6d1]368    def set_printer_attributes(self):
369        pass
[33ea505]370
371    def cancel_job(self, job_id, requesting_user_name=None):
372        job = self.get_job(job_id)
373        try:
374            job.cancel()
375        except InvalidJobStateException:
376            # XXX
377            raise
378
379    def send_document(self, job_id, document, document_name=None,
380                      document_format=None, document_natural_language=None,
381                      requesting_user_name=None, compression=None,
382                      last_document=None):
383
384        job = self.get_job(job_id)
[345c476]385        job.spool(document)
[33ea505]386
387    def send_uri(self):
388        pass
389
390    def get_job_attributes(self, job_id, requested_attributes=None):
391        if requested_attributes is None:
392            requested = self.job_attributes
393        else:
394            requested = [a for a in self.job_attributes \
395                         if a in requested_attributes]
396
397        _attributes = [attr.replace("-", "_") for attr in requested]
398        attributes = [getattr(self, attr)(job_id) for attr in _attributes]
399        return attributes
400
401    def set_job_attributes(self):
402        pass
403
404    def restart_job(self):
405        pass
406
[c500bc2]407    def promote_job(self, job_id):
408        # According to RFC 3998, we need to put the job at the front
409        # of the queue (so that when the currently playing job
410        # completes, this one will go next
411       
412        job = self.get_job(job_id)
413        job.priority = 1 # XXX we need to actually do something
414                         # correct here
Note: See TracBrowser for help on using the repository browser.