allow conversion/processing of CSV file to XLSX upon download

This commit is contained in:
Helmut Merz 2017-11-14 17:41:56 +01:00
parent 7f7a2af25f
commit 516eda33d1
4 changed files with 53 additions and 15 deletions

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2016 Helmut Merz helmutm@cy55.de # Copyright (c) 2017 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -22,13 +22,18 @@ View classes for export of report results.
import csv import csv
from cStringIO import StringIO from cStringIO import StringIO
import os
import time
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.i18n import translate from zope.i18n import translate
from zope.traversing.api import getName
from cybertools.meta.interfaces import IOptions
from cybertools.util.date import formatTimeStamp
from loops.common import normalizeName from loops.common import normalizeName
from loops.expert.browser.report import ResultsConceptView from loops.expert.browser.report import ResultsConceptView
from loops.interfaces import ILoopsObject from loops.interfaces import ILoopsObject
from loops.util import _ from loops.util import _, getVarDirectory
class ResultsConceptCSVExport(ResultsConceptView): class ResultsConceptCSVExport(ResultsConceptView):
@ -55,10 +60,34 @@ class ResultsConceptCSVExport(ResultsConceptView):
lang = self.languageInfo.language lang = self.languageInfo.language
return translate(_(field.title), target_language=lang) return translate(_(field.title), target_language=lang)
def getFilepaths(self, name):
repName = getName(self.report.context)
ts = formatTimeStamp(None, format='%y%m%d%H%M%S')
name = '-'.join((ts, repName))
return (name + '.csv',
name + '.xlsx',
repName + '.ods')
def renderCsv(self, name, src, tgt, tpl):
callable = os.path.join('~', 'bin', name)
command = ' '.join((callable, src, tgt, tpl))
#print '***', command
os.popen(command).read()
def __call__(self): def __call__(self):
fields = self.displayedColumns fields = self.displayedColumns
fieldNames = [f.name for f in fields] fieldNames = [f.name for f in fields]
output = StringIO() csvRenderer = IOptions(self.report)('csv_renderer')
if not csvRenderer:
csvRenderer = self.globalOptions('csv_renderer')
if csvRenderer:
csvRenderer = csvRenderer[0]
outfn, inpfn, tplfn = self.getFilepaths(csvRenderer)
outpath = os.path.join(getVarDirectory(), 'export', 'excel', outfn)
inpath = os.path.join(getVarDirectory(), 'export', 'excel', inpfn)
output = open(outpath, 'w')
else:
output = StringIO()
writer = csv.DictWriter(output, fieldNames, delimiter=self.delimiter) writer = csv.DictWriter(output, fieldNames, delimiter=self.delimiter)
output.write(self.delimiter.join( output.write(self.delimiter.join(
[self.getColumnTitle(f) for f in fields]) + '\n') [self.getColumnTitle(f) for f in fields]) + '\n')
@ -73,18 +102,28 @@ class ResultsConceptCSVExport(ResultsConceptView):
value = encode(value, self.encoding) value = encode(value, self.encoding)
data[f.name] = value data[f.name] = value
writer.writerow(data) writer.writerow(data)
text = output.getvalue() if csvRenderer:
self.setDownloadHeader(text) output.close()
self.renderCsv(csvRenderer, outfn, inpfn, tplfn)
input = open(inpath, 'rb')
text = input.read()
input.close()
self.setDownloadHeader(text)
#'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
#'xlsx')
else:
text = output.getvalue()
self.setDownloadHeader(text)
return text return text
def setDownloadHeader(self, text): def setDownloadHeader(self, text, ctype='text/csv', ext='csv'):
response = self.request.response response = self.request.response
response.setHeader('Content-Disposition', response.setHeader('Content-Disposition',
'attachment; filename=%s.csv' % 'attachment; filename=%s.%s' %
self.getFileName()) (self.getFileName(), ext))
response.setHeader('Cache-Control', '') response.setHeader('Cache-Control', '')
response.setHeader('Pragma', '') response.setHeader('Pragma', '')
response.setHeader('Content-Type', 'text/csv') response.setHeader('Content-Type', ctype)
response.setHeader('Content-Length', len(text)) response.setHeader('Content-Length', len(text))

View file

@ -1,5 +1,3 @@
<!-- $Id$ -->
<configure <configure
xmlns="http://namespaces.zope.org/zope" xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser" xmlns:browser="http://namespaces.zope.org/browser"

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2014 Helmut Merz helmutm@cy55.de # Copyright (c) 2017 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -35,6 +35,7 @@ from cybertools.composer.report.interfaces import IReportParams
from cybertools.composer.report.result import ResultSet, Row from cybertools.composer.report.result import ResultSet, Row
from cybertools.util.jeep import Jeep from cybertools.util.jeep import Jeep
from loops.common import AdapterBase from loops.common import AdapterBase
from loops.expert.concept import IQueryConcept, QueryConcept
from loops.interfaces import ILoopsAdapter from loops.interfaces import ILoopsAdapter
from loops.type import TypeInterfaceSourceList from loops.type import TypeInterfaceSourceList
from loops import util from loops import util
@ -43,7 +44,7 @@ from loops.util import _
# interfaces # interfaces
class IReport(ILoopsAdapter, IReportParams): class IReport(ILoopsAdapter, IReportParams, IQueryConcept):
""" The report adapter for the persistent object (concept) that stores """ The report adapter for the persistent object (concept) that stores
the report in the concept map. the report in the concept map.
""" """
@ -66,7 +67,7 @@ class IReportInstance(IBaseReport):
# report concept adapter and instances # report concept adapter and instances
class Report(AdapterBase): class Report(QueryConcept):
implements(IReport) implements(IReport)

View file

@ -188,7 +188,7 @@ First we have to make sure that there is a report concept type available.
In addition we need a predicate that connects one or more tasks with a report. In addition we need a predicate that connects one or more tasks with a report.
>>> from loops.expert.report import IReport, Report >>> from loops.expert.report import IReport, Report
>>> component.provideAdapter(Report) >>> component.provideAdapter(Report, provides=IReport)
>>> tReport = addAndConfigureObject(concepts, Concept, 'report', >>> tReport = addAndConfigureObject(concepts, Concept, 'report',
... title=u'Report', conceptType=concepts.getTypeConcept(), ... title=u'Report', conceptType=concepts.getTypeConcept(),
... typeInterface=IReport) ... typeInterface=IReport)