Export of Issues to CSV

Andreas Flöter <[email protected]>
Newsgroups gmane.comp.bug-tracking.roundup.user
Message-ID <[email protected]>
Hi,

I have extended a little the "ExportCSVNames" script from the Wiki which now 
resolves also Lists and Multilink values to there clear text values. 

The date is not yet aligned to the timezone. This needs to be fixed.

My amendments are not really beautiful but I was wondering if somebody could 
incorporate this functionality into the main stream of roundup. The current 
CSV export is very basic and could be more helpful if done in this way.

Andreas

------------------------------------------------------------------------------
Subversion Kills Productivity. Get off Subversion & Make the Move to Perforce.
With Perforce, you get hassle-free workflows. Merge that actually works. 
Faster operations. Version large binaries.  Built-in WAN optimization and the
freedom to use Git, Perforce or both. Make the move to Perforce.
http://pubads.g.doubleclick.net/gampad/clk?id=122218951&iu=/4140/ostg.clktrk

_______________________________________________
Roundup-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/roundup-users
ExportCSVNamesAction.py (text/x-python, 4.7 KB)
from roundup.cgi.actions import Action
from roundup.cgi import templating
from roundup import hyperdb

import csv
import re
import codecs
import logging
import sys

LOG = logging.getLogger(__name__)

class ExportCSVNamesAction(Action):
    name = 'export'
    permissionType = 'View'
    list_sep = ';'

    def handle(self):
        ''' Export the specified search query as CSV. '''
        # figure the request
        global LOG
        LOG = self.db.get_logger()
        request = templating.HTMLRequest(self.client)
        filterspec = request.filterspec
        sort = request.sort
        group = request.group
        columns = request.columns
        klass = self.db.getclass(request.classname)

        # full-text search
        if request.search_text:
            matches = self.db.indexer.search(
                re.findall(r'\b\w{2,25}\b', request.search_text), klass)
        else:
            matches = None

        header = self.client.additional_headers
        header['Content-Type'] = 'text/csv; charset=%s' % self.client.charset
        # some browsers will honor the filename here...
        header['Content-Disposition'] = 'inline; filename=query.csv'

        self.client.header()

        if self.client.env['REQUEST_METHOD'] == 'HEAD':
            # all done, return a dummy string
            return 'dummy'
        wfile = self.client.request.wfile
        if self.client.charset != self.client.STORAGE_CHARSET:
            wfile = codecs.EncodedFile(wfile,
                self.client.STORAGE_CHARSET, self.client.charset, 'replace')
        writer = csv.writer(wfile)
        # Figure out Link columns
        represent = {}

        def repr_no_right(cls, col):
            """User doen't have the right to see the value of col."""
            def fct(arg):
                return "[hidden]"
            return fct
        def repr_link(cls, col):
            """Generate a function which returns the string representation of
            a link depending on `cls` and `col`."""
            def fct(arg):
                if arg == None:
                    return ""
                else:
                    return str(cls.get(arg, col))
            return fct
        def repr_list(cls, col):
            def fct(arg):
                if arg == None:
                    return ""
                elif type(arg) is list:
                    seq = [str(cls.get(val, col)) for val in arg]
                    return list_sep.join(seq)
            return fct
        def repr_val():
            def fct(arg):
                if arg == None:
                    return ""
                elif isinstance(arg, basestring):
                    return str(arg)
                elif type(arg) is list:
                    seq = [str(val) for val in arg]
                    return list_sep.join(seq)
                else:
                    return str(arg)
            return fct

        props = klass.getprops()
        
        LOG.debug("Determine translation map.")
        ncols = []
        for col in columns:
            LOG.debug(" %r" % col)
            ncols.append(col)
            # represent[col] = str
            represent[col] = repr_val()
            if isinstance(props[col], hyperdb.Multilink):
                cname = props[col].classname
                cclass = self.db.getclass(cname)
                represent[col] = repr_list(cclass, 'name')
            if isinstance(props[col], hyperdb.Link):
                cname = props[col].classname
                cclass = self.db.getclass(cname)
                if cclass.getprops().has_key('name'):
                    represent[col] = repr_link(cclass, 'name')
                elif cname == 'user':
                    # represent[col] = repr_link(cclass, 'username')
                    if not self.hasPermission('View', classname=cname):
                        represent[col] = repr_no_right(cclass, 'realname')
                    else:
                        represent[col] = repr_link(cclass, 'realname')
        columns = ncols
        # generate the CSV output
        self.client._socket_op(writer.writerow, columns)
        # and search
        for itemid in klass.filter(matches, filterspec, sort, group):
            row = []
            for col in columns:
                # check permission to view this property on this item
                if not self.hasPermission(self.permissionType, itemid=itemid,
                        classname=request.classname, property=col):
                    represent[col] = repr_no_right(request.classname, col)
                row.append(represent[col](klass.get(itemid, col)))
            self.client._socket_op(writer.writerow, row)
        return '\n'

def init(instance):
    instance.registerAction('export_csv_names', ExportCSVNamesAction)

# vim: set filetype=python sts=4 sw=4 et si
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.