allow conversion/processing of CSV file to XLSX upon download
This commit is contained in:
		
							parent
							
								
									7f7a2af25f
								
							
						
					
					
						commit
						516eda33d1
					
				
					 4 changed files with 53 additions and 15 deletions
				
			
		|  | @ -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)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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" | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue