source: server/lib/gutenbach/server/printer.py @ 33ea505

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

Keep IPP code in GutenbachPrinter?, not in GutenbachJob?

  • Property mode set to 100644
File size: 11.8 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       
[eee389a]71    def __init__(self, name, *args, **kwargs):
72
[d21198f]73        super(GutenbachPrinter, self).__init__(*args, **kwargs)
74       
75        self.name = name
76        self.time_created = int(time.time())
[b2e077a]77
[d21198f]78        self.finished_jobs = []
79        self.pending_jobs = []
80        self.current_job = None
81        self.jobs = {}
[b2e077a]82
[d21198f]83        self.lock = threading.RLock()
84        self.running = False
85        self.paused = False
[776a659]86
[d21198f]87        # CUPS ignores jobs with id 0, so we have to start at 1
88        self._next_job_id = 1
[b01b6d1]89
90    def __repr__(self):
91        return str(self)
92
93    def __str__(self):
94        return "<Printer '%s'>" % self.name
95
[33ea505]96    def run(self):
97        self.running = True
98        while self.running:
99            with self.lock:
100                try:
101                    if self.current_job is None:
102                        self.start_job()
103                    elif self.current_job.is_finished:
104                        self.complete_job()
105                except:
106                    logger.fatal(traceback.format_exc())
107                    sys.exit(1)
108            time.sleep(0.1)
109
[b01b6d1]110    ######################################################################
111    ###                          Properties                            ###
112    ######################################################################
113
114    @property
115    def uris(self):
116        uris = ["ipp://localhost:8000/printers/" + self.name,
117                "ipp://localhost/printers/" + self.name]
118        return uris
119   
120    @property
121    def uri(self):
122        return self.uris[0]
123
124    @property
[eee389a]125    def state(self):
126        with self.lock:
127            if self.current_job is not None:
128                val = States.PROCESSING
129            elif len(self.pending_jobs) == 0:
130                val = States.IDLE
131            else:
132                val = States.STOPPED
133        return val
134
135    @property
136    def active_jobs(self):
137        with self.lock:
138            jobs = self.pending_jobs[:]
139            if self.current_job is not None:
140                jobs.insert(0, self.current_job.id)
141        return jobs
[b01b6d1]142
143    ######################################################################
144    ###                            Methods                             ###
145    ######################################################################
146
[eee389a]147    def start_job(self):
148        with self.lock:
149            if self.current_job is None:
150                try:
151                    job_id = heapq.heappop(self.pending_jobs)
152                    self.current_job = self.get_job(job_id)
153                    self.current_job.play()
154                except IndexError:
[d21198f]155                    self.current_job = None
[eee389a]156                except InvalidJobStateException:
157                    heapq.heappush(self.pending_jobs, self.current_job.id)
158                    self.current_job = None
159                   
160    def complete_job(self):
161        with self.lock:
162            if self.current_job is None:
163                return
164
165            try:
166                if not self.current_job.is_finished:
167                    self.current_job.stop()
168            finally:
169                self.finished_jobs.append(self.current_job)
170                self.current_job = None
[1a63bf7]171
[eee389a]172    def get_job(self, job_id):
173        with self.lock:
174            if job_id not in self.jobs:
175                raise InvalidJobException(job_id)
176            job = self.jobs[job_id]
177        return job
[b01b6d1]178
179    ######################################################################
180    ###                        IPP Attributes                          ###
181    ######################################################################
[1a63bf7]182
[b2e077a]183    @property
184    def printer_uri_supported(self):
[793432f]185        return ipp.PrinterUriSupported(self.uri)
[1a63bf7]186
[b2e077a]187    @property
188    def uri_authentication_supported(self):
[793432f]189        return ipp.UriAuthenticationSupported("none")
[b2e077a]190
191    @property
192    def uri_security_supported(self):
[793432f]193        return ipp.UriSecuritySupported("none")
[b2e077a]194
195    @property
196    def printer_name(self):
[793432f]197        return ipp.PrinterName(self.name)
[1a63bf7]198
[b2e077a]199    @property
200    def printer_state(self):
[b01b6d1]201        return ipp.PrinterState(self.state)
[1a63bf7]202
[b2e077a]203    @property
204    def printer_state_reasons(self):
[793432f]205        return ipp.PrinterStateReasons("none")
[b2e077a]206
207    @property
208    def ipp_versions_supported(self):
[793432f]209        return ipp.IppVersionsSupported("1.0", "1.1")
[1a63bf7]210
[f6e2532]211    # XXX: We should query ourself for the supported operations
[b2e077a]212    @property
213    def operations_supported(self):
[793432f]214        return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS)
[b2e077a]215
216    @property
217    def charset_configured(self):
[793432f]218        return ipp.CharsetConfigured("utf-8")
[b2e077a]219
220    @property
221    def charset_supported(self):
[793432f]222        return ipp.CharsetSupported("utf-8")
[b2e077a]223
224    @property
225    def natural_language_configured(self):
[793432f]226        return ipp.NaturalLanguageConfigured("en-us")
[b2e077a]227
228    @property
229    def generated_natural_language_supported(self):
[793432f]230        return ipp.GeneratedNaturalLanguageSupported("en-us")
[b2e077a]231
232    @property
233    def document_format_default(self):
[793432f]234        return ipp.DocumentFormatDefault("application/octet-stream")
[b2e077a]235
236    @property
237    def document_format_supported(self):
[793432f]238        return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3")
[b2e077a]239
240    @property
241    def printer_is_accepting_jobs(self):
[793432f]242        return ipp.PrinterIsAcceptingJobs(True)
[b2e077a]243
244    @property
245    def queued_job_count(self):
[793432f]246        return ipp.QueuedJobCount(len(self.active_jobs))
[b2e077a]247
248    @property
249    def pdl_override_supported(self):
[793432f]250        return ipp.PdlOverrideSupported("not-attempted")
[b2e077a]251
252    @property
253    def printer_up_time(self):
[793432f]254        return ipp.PrinterUpTime(int(time.time()) - self.time_created)
[b2e077a]255
256    @property
257    def compression_supported(self):
[793432f]258        return ipp.CompressionSupported("none")
[b2e077a]259
[f6e2532]260    @property
261    def multiple_operation_time_out(self):
[793432f]262        return ipp.MultipleOperationTimeOut(240)
[f6e2532]263
264    @property
265    def multiple_document_jobs_supported(self):
[793432f]266        return ipp.MultipleDocumentJobsSupported(False)
[f6e2532]267
[33ea505]268    ######################################################################
269    ###                      Job IPP Attributes                        ###
270    ######################################################################
271
272    def job_id(self, job_id):
273        job = self.get_job(job_id)
274        return ipp.JobId(job.id)
275
276    def job_name(self, job_id):
277        job = self.get_job(job_id)
278        return ipp.JobName(job.name)
279
280    def job_originating_user_name(self, job_id):
281        job = self.get_job(job_id)
282        return ipp.JobOriginatingUserName(job.creator)
283
284    def job_k_octets(self, job_id):
285        job = self.get_job(job_id)
286        return ipp.JobKOctets(job.size)
287
288    def job_state(self, job_id):
289        job = self.get_job(job_id)
290        return ipp.JobState(job.state)
291
292    def job_printer_uri(self, job_id):
293        job = self.get_job(job_id)
294        return ipp.JobPrinterUri(self.uri)
[ee8e6d0]295
[b01b6d1]296    ######################################################################
297    ###                        IPP Operations                          ###
298    ######################################################################
299
300    def print_job(self):
301        pass
302
303    def validate_job(self):
304        pass
305
[33ea505]306    def get_jobs(self, requesting_user_name=None, which_jobs=None,
307                 requested_attributes=None):
308       
[b01b6d1]309        # Filter by the which-jobs attribute
310        if which_jobs is None:
[34a4e5d]311            which_jobs = "not-completed"
312
313        if which_jobs == "completed":
[b01b6d1]314            jobs = [self.jobs[job_id] for job_id in self.finished_jobs]
315        elif which_jobs == "not-completed":
316            jobs = [self.jobs[job_id] for job_id in self.active_jobs]
[ee8e6d0]317        else:
[b01b6d1]318            raise ipp.errors.ClientErrorAttributes(
319                which_jobs, ipp.WhichJobs(which_jobs))
[b2e077a]320
[b01b6d1]321        # Filter by username
322        if requesting_user_name is None:
323            user_jobs = jobs
324        else:
325            user_jobs = [job for job in jobs if job.creator == requesting_user_name]
[33ea505]326
327        # Get the attributes of each job
328        job_attrs = [self.get_job_attributes(
329            job.id, requested_attributes=requested_attributes) for job in user_jobs]
[ee8e6d0]330       
[33ea505]331        return job_attrs
[ee8e6d0]332
[b01b6d1]333    def print_uri(self):
334        pass
335
336    def create_job(self, requesting_user_name="", job_name="", job_k_octets=0):
[eee389a]337        job_id = self._next_job_id
338        self._next_job_id += 1
[ee8e6d0]339       
[33ea505]340        job = GutenbachJob(
341            job_id,
342            creator=requesting_user_name,
343            name=job_name)
[b01b6d1]344       
[ee8e6d0]345        self.jobs[job_id] = job
[eee389a]346        self.pending_jobs.append(job_id)
[b01b6d1]347       
[33ea505]348        return job_id
[776a659]349
[b01b6d1]350    def pause_printer(self):
[ee8e6d0]351        pass
[776a659]352
[b01b6d1]353    def resume_printer(self):
354        pass
[776a659]355
[b01b6d1]356    def get_printer_attributes(self, requested_attributes=None):
357        if requested_attributes is None:
[33ea505]358            requested = self.printer_attributes
[e58af05]359        else:
[33ea505]360            requested = [a for a in self.printer_attributes \
361                         if a in requested_attributes]
[b2e077a]362
[b01b6d1]363        _attributes = [attr.replace("-", "_") for attr in requested]
364        attributes = [getattr(self, attr) for attr in _attributes]
365        return attributes
[776a659]366
[b01b6d1]367    def set_printer_attributes(self):
368        pass
[33ea505]369
370    def cancel_job(self, job_id, requesting_user_name=None):
371        job = self.get_job(job_id)
372        try:
373            job.cancel()
374        except InvalidJobStateException:
375            # XXX
376            raise
377
378    def send_document(self, job_id, document, document_name=None,
379                      document_format=None, document_natural_language=None,
380                      requesting_user_name=None, compression=None,
381                      last_document=None):
382
383        job = self.get_job(job_id)
384        job.spool(document, username=requesting_user_name)
385
386    def send_uri(self):
387        pass
388
389    def get_job_attributes(self, job_id, requested_attributes=None):
390        if requested_attributes is None:
391            requested = self.job_attributes
392        else:
393            requested = [a for a in self.job_attributes \
394                         if a in requested_attributes]
395
396        _attributes = [attr.replace("-", "_") for attr in requested]
397        attributes = [getattr(self, attr)(job_id) for attr in _attributes]
398        return attributes
399
400    def set_job_attributes(self):
401        pass
402
403    def restart_job(self):
404        pass
405
406    def promote_job(self):
407        pass
Note: See TracBrowser for help on using the repository browser.