root / pykota / trunk / pykota / dumper.py @ 2536

Revision 2536, 11.3 kB (checked in by jerome, 19 years ago)

In fact, there was NO bug at all, only me misreading the command's help :-)
Severity : I'm stupid !

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota Print Quota Data Dumper
2#
3# PyKota - Print Quotas for CUPS and LPRng
4#
5# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19#
20# $Id$
21#
22#
23
24import sys
25import os
26import pwd
27from xml.sax import saxutils
28
29from mx import DateTime
30
31try :
32    import jaxml
33except ImportError :   
34    sys.stderr.write("The jaxml Python module is not installed. XML output is disabled.\n")
35    sys.stderr.write("Download jaxml from http://www.librelogiciel.com/software/ or from your Debian archive of choice\n")
36    hasJAXML = 0
37else :   
38    hasJAXML = 1
39
40from pykota import version
41from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, N_
42
43class DumPyKota(PyKotaTool) :       
44    """A class for dumpykota."""
45    validdatatypes = { "history" : N_("History"),
46                       "users" : N_("Users"),
47                       "groups" : N_("Groups"),
48                       "printers" : N_("Printers"),
49                       "upquotas" : N_("Users Print Quotas"),
50                       "gpquotas" : N_("Users Groups Print Quotas"),
51                       "payments" : N_("History of Payments"),
52                       "pmembers" : N_("Printers Groups Membership"), 
53                       "umembers" : N_("Users Groups Membership"),
54                       "billingcodes" : N_("Billing Codes"),
55                     }
56    validformats = { "csv" : N_("Comma Separated Values"),
57                     "ssv" : N_("Semicolon Separated Values"),
58                     "tsv" : N_("Tabulation Separated Values"),
59                     "xml" : N_("eXtensible Markup Language"),
60                     "cups" : N_("CUPS' page_log"),
61                   } 
62    validfilterkeys = [ "username",
63                        "groupname",
64                        "printername",
65                        "pgroupname",
66                        "hostname",
67                        "billingcode",
68                        "start",
69                        "end",
70                      ]
71    def main(self, arguments, options, restricted=1) :
72        """Print Quota Data Dumper."""
73        if restricted and not self.config.isAdmin :
74            raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
75           
76        extractonly = {}
77        for filterexp in arguments :
78            if filterexp.strip() :
79                try :
80                    (filterkey, filtervalue) = [part.strip() for part in filterexp.split("=")]
81                    if filterkey not in self.validfilterkeys :
82                        raise ValueError               
83                except ValueError :   
84                    raise PyKotaCommandLineError, _("Invalid filter value [%s], see help.") % filterexp
85                else :   
86                    extractonly.update({ filterkey : filtervalue })
87           
88        datatype = options["data"]
89        if datatype not in self.validdatatypes.keys() :
90            raise PyKotaCommandLineError, _("Invalid modifier [%s] for --data command line option, see help.") % datatype
91                   
92        format = options["format"]
93        if format not in self.validformats.keys() \
94           or ((format == "cups") and (datatype == "history") and options["sum"]) :
95            raise PyKotaCommandLineError, _("Invalid modifier [%s] for --format command line option, see help.") % format
96           
97        if (format == "xml") and not hasJAXML :
98            raise PyKotaToolError, _("XML output is disabled because the jaxml module is not available.")
99           
100        if options["sum"] and datatype not in ("payments", "history") : 
101            raise PyKotaCommandLineError, _("Invalid data type [%s] for --sum command line option, see help.") % datatype
102           
103        entries = getattr(self.storage, "extract%s" % datatype.title())(extractonly)
104        if entries :
105            mustclose = 0   
106            if options["output"].strip() == "-" :   
107                self.outfile = sys.stdout
108            else :   
109                self.outfile = open(options["output"], "w")
110                mustclose = 1
111               
112            retcode = getattr(self, "dump%s" % format.title())(self.summarizeDatas(entries, datatype, extractonly, options["sum"]), datatype)
113           
114            if mustclose :
115                self.outfile.close()
116               
117            return retcode   
118        return 0
119       
120    def summarizeDatas(self, entries, datatype, extractonly, sum=0) :   
121        """Transforms the datas into a summarized view (with totals).
122       
123           If sum is false, returns the entries unchanged.
124        """   
125        if not sum :
126            return entries
127        else :   
128            headers = entries[0]
129            nbheaders = len(headers)
130            fieldnumber = {}
131            fieldname = {}
132            for i in range(nbheaders) :
133                fieldnumber[headers[i]] = i
134           
135            if datatype == "payments" :
136                totalize = [ ("amount", float) ]
137                keys = [ "username" ]
138            else : # elif datatype == "history"
139                totalize = [ ("jobsize", int), 
140                             ("jobprice", float),
141                             ("jobsizebytes", int),
142                           ]
143                keys = [ k for k in ("username", "printername", "hostname", "billingcode") if k in extractonly.keys() ]
144               
145            newentries = [ headers ]
146            sortedentries = entries[1:]
147            if keys :
148                # If we have several keys, we can sort only on the first one, because they
149                # will vary the same way.
150                sortedentries.sort(lambda x, y, fnum=fieldnumber[keys[0]] : cmp(x[fnum], y[fnum]))
151            totals = {}
152            for (k, t) in totalize :
153                totals[k] = { "convert" : t, "value" : 0.0 }
154            prevkeys = {}
155            for k in keys :
156                prevkeys[k] = sortedentries[0][fieldnumber[k]]
157            for entry in sortedentries :
158                curval = '-'.join([str(entry[fieldnumber[k]]) for k in keys])
159                prevval = '-'.join([str(prevkeys[k]) for k in keys])
160                if curval != prevval :
161                    summary = [ "*" ] * nbheaders
162                    for k in keys :
163                        summary[fieldnumber[k]] = prevkeys[k]
164                    for k in totals.keys() :   
165                        summary[fieldnumber[k]] = totals[k]["convert"](totals[k]["value"])
166                    newentries.append(summary)
167                    for k in totals.keys() :   
168                        totals[k]["value"] = totals[k]["convert"](entry[fieldnumber[k]])
169                else :   
170                    for k in totals.keys() :   
171                        totals[k]["value"] += totals[k]["convert"](entry[fieldnumber[k]])
172                for k in keys :
173                    prevkeys[k] = entry[fieldnumber[k]]
174            summary = [ "*" ] * nbheaders
175            for k in keys :
176                summary[fieldnumber[k]] = prevkeys[k]
177            for k in totals.keys() :   
178                summary[fieldnumber[k]] = totals[k]["convert"](totals[k]["value"])
179            newentries.append(summary)
180            return newentries
181           
182    def dumpWithSeparator(self, separator, entries) :   
183        """Dumps datas with a separator."""
184        for entry in entries :
185            line = []
186            for value in entry :
187                if type(value).__name__ in ("str", "NoneType") :
188                    line.append('"%s"' % str(value).replace(separator, "\\%s" % separator).replace('"', '\\"'))
189                else :   
190                    line.append(str(value))
191            try :
192                self.outfile.write("%s\n" % separator.join(line))
193            except IOError, msg :   
194                sys.stderr.write("%s : %s\n" % (_("PyKota data dumper failed : I/O error"), msg))
195                return -1
196        return 0       
197       
198    def dumpCsv(self, entries, dummy) :   
199        """Dumps datas with a comma as the separator."""
200        return self.dumpWithSeparator(",", entries)
201                           
202    def dumpSsv(self, entries, dummy) :   
203        """Dumps datas with a comma as the separator."""
204        return self.dumpWithSeparator(";", entries)
205                           
206    def dumpTsv(self, entries, dummy) :   
207        """Dumps datas with a comma as the separator."""
208        return self.dumpWithSeparator("\t", entries)
209       
210    def dumpCups(self, entries, dummy) :   
211        """Dumps history datas as CUPS' page_log format."""
212        fieldnames = entries[0]
213        fields = {}
214        for i in range(len(fieldnames)) :
215            fields[fieldnames[i]] = i
216        sortindex = fields["jobdate"]   
217        entries = entries[1:]
218        entries.sort(lambda m,n,si=sortindex : cmp(m[si], n[si]))
219        for entry in entries :   
220            printername = entry[fields["printername"]]
221            username = entry[fields["username"]]
222            jobid = entry[fields["jobid"]]
223            jobdate = DateTime.ISO.ParseDateTime(entry[fields["jobdate"]])
224            gmtoffset = jobdate.gmtoffset()
225            jobdate = "%s %+03i00" % (jobdate.strftime("%d/%b/%Y:%H:%M:%S"), gmtoffset.hour)
226            jobsize = entry[fields["jobsize"]] or 0
227            copies = entry[fields["copies"]] or 1
228            hostname = entry[fields["hostname"]] or ""
229            billingcode = entry[fields["billingcode"]] or "-"
230            for pagenum in range(1, jobsize+1) :
231                self.outfile.write("%s %s %s [%s] %s %s %s %s\n" % (printername, username, jobid, jobdate, pagenum, copies, billingcode, hostname))
232       
233    def dumpXml(self, entries, datatype) :   
234        """Dumps datas as XML."""
235        x = jaxml.XML_document(encoding="UTF-8")
236        x.pykota(version=version.__version__, author=version.__author__)
237        x.dump(storage=self.config.getStorageBackend()["storagebackend"], type=datatype)
238        headers = entries[0]
239        for entry in entries[1:] :
240            x._push()
241            x.entry()
242            for (header, value) in zip(headers, entry) :
243                strvalue = str(value)
244                typval = type(value).__name__
245                if header in ("filename", "title", "options", "billingcode") \
246                          and (typval == "str") :
247                    try :
248                        strvalue = unicode(strvalue, self.getCharset()).encode("UTF-8")
249                    except UnicodeError :   
250                        pass
251                    strvalue = saxutils.escape(strvalue, { "'" : "&apos;", \
252                                                           '"' : "&quot;" })
253                x.attribute(strvalue, type=typval, name=header)
254            x._pop()   
255        x._output(self.outfile)
Note: See TracBrowser for help on using the browser.