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
Line 
1from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
2from .job import GutenbachJob
3from gutenbach.ipp import PrinterStates as States
4import gutenbach.ipp as ipp
5import logging
6import time
7import threading
8import heapq
9import traceback
10import sys
11
12
13# initialize logger
14logger = logging.getLogger(__name__)
15
16class GutenbachPrinter(threading.Thread):
17
18    # for IPP
19    printer_attributes = [
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",
38        "compression-supported",
39        "multiple-operation-time-out",
40        "multiple-document-jobs-supported",
41    ]
42
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
52    operations = [
53        "print-job",
54        "validate-job",
55        "get-jobs",
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"
69    ]
70       
71    def __init__(self, name, *args, **kwargs):
72
73        super(GutenbachPrinter, self).__init__(*args, **kwargs)
74       
75        self.name = name
76        self.time_created = int(time.time())
77
78        self.finished_jobs = []
79        self.pending_jobs = []
80        self.current_job = None
81        self.jobs = {}
82
83        self.lock = threading.RLock()
84        self.running = False
85        self.paused = False
86
87        # CUPS ignores jobs with id 0, so we have to start at 1
88        self._next_job_id = 1
89
90    def __repr__(self):
91        return str(self)
92
93    def __str__(self):
94        return "<Printer '%s'>" % self.name
95
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
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
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
142
143    ######################################################################
144    ###                            Methods                             ###
145    ######################################################################
146
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:
155                    self.current_job = None
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
171
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
178
179    ######################################################################
180    ###                        IPP Attributes                          ###
181    ######################################################################
182
183    @property
184    def printer_uri_supported(self):
185        return ipp.PrinterUriSupported(self.uri)
186
187    @property
188    def uri_authentication_supported(self):
189        return ipp.UriAuthenticationSupported("none")
190
191    @property
192    def uri_security_supported(self):
193        return ipp.UriSecuritySupported("none")
194
195    @property
196    def printer_name(self):
197        return ipp.PrinterName(self.name)
198
199    @property
200    def printer_state(self):
201        return ipp.PrinterState(self.state)
202
203    @property
204    def printer_state_reasons(self):
205        return ipp.PrinterStateReasons("none")
206
207    @property
208    def ipp_versions_supported(self):
209        return ipp.IppVersionsSupported("1.0", "1.1")
210
211    # XXX: We should query ourself for the supported operations
212    @property
213    def operations_supported(self):
214        return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS)
215
216    @property
217    def charset_configured(self):
218        return ipp.CharsetConfigured("utf-8")
219
220    @property
221    def charset_supported(self):
222        return ipp.CharsetSupported("utf-8")
223
224    @property
225    def natural_language_configured(self):
226        return ipp.NaturalLanguageConfigured("en-us")
227
228    @property
229    def generated_natural_language_supported(self):
230        return ipp.GeneratedNaturalLanguageSupported("en-us")
231
232    @property
233    def document_format_default(self):
234        return ipp.DocumentFormatDefault("application/octet-stream")
235
236    @property
237    def document_format_supported(self):
238        return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3")
239
240    @property
241    def printer_is_accepting_jobs(self):
242        return ipp.PrinterIsAcceptingJobs(True)
243
244    @property
245    def queued_job_count(self):
246        return ipp.QueuedJobCount(len(self.active_jobs))
247
248    @property
249    def pdl_override_supported(self):
250        return ipp.PdlOverrideSupported("not-attempted")
251
252    @property
253    def printer_up_time(self):
254        return ipp.PrinterUpTime(int(time.time()) - self.time_created)
255
256    @property
257    def compression_supported(self):
258        return ipp.CompressionSupported("none")
259
260    @property
261    def multiple_operation_time_out(self):
262        return ipp.MultipleOperationTimeOut(240)
263
264    @property
265    def multiple_document_jobs_supported(self):
266        return ipp.MultipleDocumentJobsSupported(False)
267
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)
295
296    ######################################################################
297    ###                        IPP Operations                          ###
298    ######################################################################
299
300    def print_job(self):
301        pass
302
303    def validate_job(self):
304        pass
305
306    def get_jobs(self, requesting_user_name=None, which_jobs=None,
307                 requested_attributes=None):
308       
309        # Filter by the which-jobs attribute
310        if which_jobs is None:
311            which_jobs = "not-completed"
312
313        if which_jobs == "completed":
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]
317        else:
318            raise ipp.errors.ClientErrorAttributes(
319                which_jobs, ipp.WhichJobs(which_jobs))
320
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]
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]
330       
331        return job_attrs
332
333    def print_uri(self):
334        pass
335
336    def create_job(self, requesting_user_name="", job_name="", job_k_octets=0):
337        job_id = self._next_job_id
338        self._next_job_id += 1
339       
340        job = GutenbachJob(
341            job_id,
342            creator=requesting_user_name,
343            name=job_name)
344       
345        self.jobs[job_id] = job
346        self.pending_jobs.append(job_id)
347       
348        return job_id
349
350    def pause_printer(self):
351        pass
352
353    def resume_printer(self):
354        pass
355
356    def get_printer_attributes(self, requested_attributes=None):
357        if requested_attributes is None:
358            requested = self.printer_attributes
359        else:
360            requested = [a for a in self.printer_attributes \
361                         if a in requested_attributes]
362
363        _attributes = [attr.replace("-", "_") for attr in requested]
364        attributes = [getattr(self, attr) for attr in _attributes]
365        return attributes
366
367    def set_printer_attributes(self):
368        pass
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.