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

Revision 2293, 10.9 kB (checked in by jerome, 19 years ago)

First draft of generic code for the --sum option handling

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[2016]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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19#
20# $Id$
21#
[2034]22#
[2016]23
24import sys
25import os
26import pwd
[2264]27from xml.sax import saxutils
28
[2016]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, 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                     }
55    validformats = { "csv" : N_("Comma Separated Values"),
56                     "ssv" : N_("Semicolon Separated Values"),
57                     "tsv" : N_("Tabulation Separated Values"),
58                     "xml" : N_("eXtensible Markup Language"),
59                     "cups" : N_("CUPS' page_log"),
60                   } 
61    validfilterkeys = [ "username",
62                        "groupname",
63                        "printername",
64                        "pgroupname",
[2218]65                        "hostname",
66                        "billingcode",
[2266]67                        "start",
68                        "end",
[2016]69                      ]
[2032]70    def main(self, arguments, options, restricted=1) :
[2016]71        """Print Quota Data Dumper."""
[2032]72        if restricted and not self.config.isAdmin :
[2016]73            raise PyKotaToolError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
74           
75        extractonly = {}
76        for filterexp in arguments :
77            if filterexp.strip() :
78                try :
79                    (filterkey, filtervalue) = [part.strip() for part in filterexp.split("=")]
80                    if filterkey not in self.validfilterkeys :
81                        raise ValueError               
82                except ValueError :   
[2218]83                    raise PyKotaToolError, _("Invalid filter value [%s], see help.") % filterexp
[2016]84                else :   
85                    extractonly.update({ filterkey : filtervalue })
86           
87        datatype = options["data"]
88        if datatype not in self.validdatatypes.keys() :
89            raise PyKotaToolError, _("Invalid modifier [%s] for --data command line option, see help.") % datatype
90                   
91        format = options["format"]
92        if (format not in self.validformats.keys()) \
93              or ((format == "cups") and (datatype != "history")) :
94            raise PyKotaToolError, _("Invalid modifier [%s] for --format command line option, see help.") % format
95           
96        if (format == "xml") and not hasJAXML :
97            raise PyKotaToolError, _("XML output is disabled because the jaxml module is not available.")
98           
[2285]99        if options["sum"] and datatype not in ("payments", "history") : 
[2286]100            raise PyKotaToolError, _("Invalid data type [%s] for --sum command line option, see help.") % datatype
[2285]101           
[2218]102        entries = getattr(self.storage, "extract%s" % datatype.title())(extractonly)
[2016]103        if entries :
104            mustclose = 0   
105            if options["output"].strip() == "-" :   
106                self.outfile = sys.stdout
107            else :   
108                self.outfile = open(options["output"], "w")
109                mustclose = 1
110               
[2285]111            retcode = getattr(self, "dump%s" % format.title())(self.summarizeDatas(entries, datatype, options["sum"]), datatype)
[2016]112           
113            if mustclose :
114                self.outfile.close()
115               
116            return retcode   
117        return 0
118       
[2285]119    def summarizeDatas(self, entries, datatype, sum=0) :   
120        """Transforms the datas into a summarized view (with totals).
121       
122           If sum is false, returns the entries unchanged.
123        """   
124        if not sum :
125            return entries
126        else :   
[2289]127            headers = entries[0]
128            nbheaders = len(headers)
129            fieldnumber = {}
[2293]130            fieldname = {}
[2289]131            for i in range(nbheaders) :
[2293]132                name = headers[i]
133                fieldnumber[name] = i
134                fieldname[i] = name
135               
[2289]136            if datatype == "payments" :
[2293]137                totalize = [ ("amount", float) ]
138                ignored = [ "date" ]
139                key = "username"
140               
141                fnkey = fieldnumber[key]
[2289]142                newentries = [ headers ]
143                sortedentries = entries[1:]
[2293]144                sortedentries.sort(lambda x, y, fnum=fnkey : cmp(x[fnum], y[fnum]))
145                totals = {}
146                for (k, t) in totalize :
147                    totals[k] = { "convert" : t, "value" : 0.0 }
148                prevkey = sortedentries[0][fnkey]
[2289]149                for entry in sortedentries :
[2293]150                    if entry[fnkey] != prevkey :
[2289]151                        summary = [None] * nbheaders
[2293]152                        summary[fnkey] = prevkey
153                        for ignore in ignored :
154                            summary[fieldnumber[ignore]] = '*'
155                        for k in totals.keys() :   
156                            summary[fieldnumber[k]] = totals[k]["convert"](totals[k]["value"])
[2289]157                        newentries.append(summary)
[2293]158                        for k in totals.keys() :   
159                            totals[k]["value"] = totals[k]["convert"](entry[fieldnumber[k]])
[2289]160                    else :   
[2293]161                        for k in totals.keys() :   
162                            totals[k]["value"] += totals[k]["convert"](entry[fieldnumber[k]])
163                    prevkey = entry[fnkey]   
[2289]164                summary = [None] * nbheaders
[2293]165                summary[fnkey] = prevkey
166                for ignore in ignored :
167                    summary[fieldnumber[ignore]] = '*'
168                for k in totals.keys() :   
169                    summary[fieldnumber[k]] = totals[k]["convert"](totals[k]["value"])
[2289]170                newentries.append(summary)
171            elif datatype == "history" :
172                newentries = entries # Fake this for now
173            else :
174                raise PyKotaToolError, _("Summarizing is not implemented for the [%s] data type, sorry.") % datatype
175            return newentries
[2285]176           
[2016]177    def dumpWithSeparator(self, separator, entries) :   
178        """Dumps datas with a separator."""
179        for entry in entries :
[2264]180            line = []
181            for value in entry :
182                if type(value).__name__ in ("str", "NoneType") :
183                    line.append('"%s"' % str(value).replace(separator, "\\%s" % separator).replace('"', '\\"'))
184                else :   
185                    line.append(str(value))
[2016]186            try :
[2264]187                self.outfile.write("%s\n" % separator.join(line))
[2016]188            except IOError, msg :   
189                sys.stderr.write("%s : %s\n" % (_("PyKota data dumper failed : I/O error"), msg))
190                return -1
191        return 0       
192       
193    def dumpCsv(self, entries, dummy) :   
194        """Dumps datas with a comma as the separator."""
195        return self.dumpWithSeparator(",", entries)
196                           
197    def dumpSsv(self, entries, dummy) :   
198        """Dumps datas with a comma as the separator."""
199        return self.dumpWithSeparator(";", entries)
200                           
201    def dumpTsv(self, entries, dummy) :   
202        """Dumps datas with a comma as the separator."""
203        return self.dumpWithSeparator("\t", entries)
204       
205    def dumpCups(self, entries, dummy) :   
206        """Dumps history datas as CUPS' page_log format."""
207        fieldnames = entries[0]
208        fields = {}
209        for i in range(len(fieldnames)) :
210            fields[fieldnames[i]] = i
211        sortindex = fields["jobdate"]   
212        entries = entries[1:]
[2034]213        entries.sort(lambda m,n,si=sortindex : cmp(m[si], n[si]))
[2219]214        for entry in entries :   
[2016]215            printername = entry[fields["printername"]]
216            username = entry[fields["username"]]
217            jobid = entry[fields["jobid"]]
218            jobdate = DateTime.ISO.ParseDateTime(entry[fields["jobdate"]])
219            gmtoffset = jobdate.gmtoffset()
220            jobdate = "%s %+03i00" % (jobdate.strftime("%d/%b/%Y:%H:%M:%S"), gmtoffset.hour)
221            jobsize = entry[fields["jobsize"]] or 0
222            copies = entry[fields["copies"]] or 1
223            hostname = entry[fields["hostname"]] or ""
[2217]224            billingcode = entry[fields["billingcode"]] or "-"
[2163]225            for pagenum in range(1, jobsize+1) :
[2217]226                self.outfile.write("%s %s %s [%s] %s %s %s %s\n" % (printername, username, jobid, jobdate, pagenum, copies, billingcode, hostname))
[2016]227       
228    def dumpXml(self, entries, datatype) :   
229        """Dumps datas as XML."""
230        x = jaxml.XML_document(encoding="UTF-8")
231        x.pykota(version=version.__version__, author=version.__author__)
232        x.dump(storage=self.config.getStorageBackend()["storagebackend"], type=datatype)
233        headers = entries[0]
234        for entry in entries[1:] :
235            x._push()
236            x.entry()
[2264]237            for (header, value) in zip(headers, entry) :
238                strvalue = str(value)
239                typval = type(value).__name__
240                if header in ("filename", "title", "options", "billingcode") \
241                          and (typval == "str") :
242                    try :
243                        strvalue = unicode(strvalue, self.getCharset()).encode("UTF-8")
244                    except UnicodeError :   
245                        pass
246                    strvalue = saxutils.escape(strvalue, { "'" : "&apos;", \
247                                                           '"' : "&quot;" })
248                x.attribute(strvalue, type=typval, name=header)
[2016]249            x._pop()   
250        x._output(self.outfile)
Note: See TracBrowser for help on using the browser.