provide CSV download view for reports; use for work and qualification listings
This commit is contained in:
		
							parent
							
								
									1716990d26
								
							
						
					
					
						commit
						e16f3e5ccf
					
				
					 7 changed files with 134 additions and 2 deletions
				
			
		
							
								
								
									
										77
									
								
								expert/browser/export.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								expert/browser/export.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | # | ||||||
|  | #  Copyright (c) 2015 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  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 | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  | View classes for export of report results. | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | import csv | ||||||
|  | from cStringIO import StringIO | ||||||
|  | from zope.i18n import translate | ||||||
|  | 
 | ||||||
|  | from loops.expert.browser.report import ReportConceptView | ||||||
|  | from loops.interfaces import ILoopsObject | ||||||
|  | from loops.util import _ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ReportConceptCSVExport(ReportConceptView): | ||||||
|  | 
 | ||||||
|  |     isToplevel = True | ||||||
|  |     reportMode = 'export' | ||||||
|  | 
 | ||||||
|  |     delimiter = ';' | ||||||
|  |     encoding = 'UTF-8' | ||||||
|  | 
 | ||||||
|  |     def getFileName(self): | ||||||
|  |         return 'output' | ||||||
|  | 
 | ||||||
|  |     def getColumnTitle(self, field): | ||||||
|  |         lang = self.languageInfo.language | ||||||
|  |         return translate(_(field.title), target_language=lang) | ||||||
|  | 
 | ||||||
|  |     def __call__(self): | ||||||
|  |         fields = self.displayedColumns | ||||||
|  |         fieldNames = [f.name for f in fields] | ||||||
|  |         output = StringIO() | ||||||
|  |         writer = csv.DictWriter(output, fieldNames, delimiter=self.delimiter) | ||||||
|  |         output.write(self.delimiter.join( | ||||||
|  |                         [self.getColumnTitle(f) for f in fields]) + '\n') | ||||||
|  |         results = self.reportInstance.getResults() | ||||||
|  |         for row in results: | ||||||
|  |             data = {} | ||||||
|  |             for f in fields: | ||||||
|  |                 value = f.getValue(row) | ||||||
|  |                 if ILoopsObject.providedBy(value): | ||||||
|  |                     value = value.title | ||||||
|  |                 if isinstance(value, unicode): | ||||||
|  |                     value = value.encode(self.encoding) | ||||||
|  |                 data[f.name] = value | ||||||
|  |             writer.writerow(data) | ||||||
|  |         text = output.getvalue() | ||||||
|  |         self.setDownloadHeader(text) | ||||||
|  |         return text | ||||||
|  | 
 | ||||||
|  |     def setDownloadHeader(self, text): | ||||||
|  |         response = self.request.response | ||||||
|  |         response.setHeader('Content-Disposition', | ||||||
|  |                            'attachment; filename=%s.csv' % | ||||||
|  |                                     self.getFileName()) | ||||||
|  |         response.setHeader('Cache-Control', '') | ||||||
|  |         response.setHeader('Pragma', '') | ||||||
|  |         response.setHeader('Content-Type', 'text/csv') | ||||||
|  |         response.setHeader('Content-Length', len(text)) | ||||||
|  | @ -22,6 +22,7 @@ Field definitions for reports. | ||||||
| 
 | 
 | ||||||
| from zope.app.form.browser.interfaces import ITerms | from zope.app.form.browser.interfaces import ITerms | ||||||
| from zope import component | from zope import component | ||||||
|  | from zope.i18n import translate | ||||||
| from zope.i18n.locales import locales | from zope.i18n.locales import locales | ||||||
| from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder | from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder | ||||||
| 
 | 
 | ||||||
|  | @ -92,6 +93,11 @@ class DateField(Field): | ||||||
|     format = ('date', 'short') |     format = ('date', 'short') | ||||||
|     cssClass = 'center' |     cssClass = 'center' | ||||||
| 
 | 
 | ||||||
|  |     def getValue(self, row): | ||||||
|  |         if getattr(row.parent.context.view, 'reportMode', None) == 'export': | ||||||
|  |             return self.getDisplayValue(row) | ||||||
|  |         super(DateField, self).getValue(row) | ||||||
|  | 
 | ||||||
|     def getDisplayValue(self, row): |     def getDisplayValue(self, row): | ||||||
|         value = self.getRawValue(row) |         value = self.getRawValue(row) | ||||||
|         if not value: |         if not value: | ||||||
|  | @ -132,6 +138,15 @@ class WorkItemStateField(Field): | ||||||
|     statesDefinition = 'workItemStates' |     statesDefinition = 'workItemStates' | ||||||
|     renderer = 'workitem_state' |     renderer = 'workitem_state' | ||||||
| 
 | 
 | ||||||
|  |     def getValue(self, row): | ||||||
|  |         view = row.parent.context.view | ||||||
|  |         if getattr(view, 'reportMode', None) == 'export': | ||||||
|  |             stateObject = row.context.getStateObject() | ||||||
|  |             lang = view.languageInfo.language | ||||||
|  |             return translate(util._(stateObject.title), target_language=lang) | ||||||
|  |         return super(WorkItemStateField, self).getValue(row) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     def getDisplayValue(self, row): |     def getDisplayValue(self, row): | ||||||
|         if row.context is None: |         if row.context is None: | ||||||
|             return None |             return None | ||||||
|  | @ -241,14 +256,18 @@ class TrackDateField(Field): | ||||||
|     cssClass = 'right' |     cssClass = 'right' | ||||||
| 
 | 
 | ||||||
|     def getValue(self, row): |     def getValue(self, row): | ||||||
|  |         reportMode = getattr(row.parent.context.view, 'reportMode', None) | ||||||
|  |         if reportMode == 'export': | ||||||
|  |             return self.getDisplayValue(row) | ||||||
|         value = self.getRawValue(row) |         value = self.getRawValue(row) | ||||||
|         if not value: |         if not value: | ||||||
|             return None |             return None | ||||||
|         return timeStamp2Date(value) |         return timeStamp2Date(value) | ||||||
| 
 | 
 | ||||||
|     def getDisplayValue(self, row): |     def getDisplayValue(self, row): | ||||||
|         value = self.getValue(row) |         value = self.getRawValue(row) | ||||||
|         if value: |         if value: | ||||||
|  |             value = timeStamp2Date(value) | ||||||
|             view = row.parent.context.view |             view = row.parent.context.view | ||||||
|             return formatDate(value, self.part, self.format, |             return formatDate(value, self.part, self.format, | ||||||
|                               view.languageInfo.language) |                               view.languageInfo.language) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| # | # | ||||||
| #  Copyright (c) 2014 Helmut Merz helmutm@cy55.de | #  Copyright (c) 2015 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 | ||||||
|  | @ -26,6 +26,7 @@ from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| 
 | 
 | ||||||
| from loops.browser.concept import ConceptView | from loops.browser.concept import ConceptView | ||||||
|  | from loops.expert.browser.export import ReportConceptCSVExport | ||||||
| from loops.expert.browser.report import ResultsConceptView | from loops.expert.browser.report import ResultsConceptView | ||||||
| from loops.organize.party import getPersonForUser | from loops.organize.party import getPersonForUser | ||||||
| from loops.util import _ | from loops.util import _ | ||||||
|  | @ -35,3 +36,8 @@ class Qualifications(ResultsConceptView): | ||||||
| 
 | 
 | ||||||
|     reportName = 'qualification_overview' |     reportName = 'qualification_overview' | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class QualificationsCSVExport(ReportConceptCSVExport): | ||||||
|  | 
 | ||||||
|  |     reportName = 'qualification_overview' | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -23,6 +23,12 @@ | ||||||
|         factory="loops.knowledge.qualification.browser.Qualifications" |         factory="loops.knowledge.qualification.browser.Qualifications" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
| 
 | 
 | ||||||
|  |   <browser:page | ||||||
|  |       name="qualifications.csv" | ||||||
|  |       for="loops.organize.interfaces.IConceptSchema" | ||||||
|  |       class="loops.knowledge.qualification.browser.QualificationsCSVExport" | ||||||
|  |       permission="zope.View" /> | ||||||
|  | 
 | ||||||
|   <!-- reports --> |   <!-- reports --> | ||||||
| 
 | 
 | ||||||
|   <zope:adapter  |   <zope:adapter  | ||||||
|  |  | ||||||
|  | @ -234,6 +234,18 @@ The user interface is a ReportConceptView subclass that is directly associated w | ||||||
|   >>> results.totals.data |   >>> results.totals.data | ||||||
|   {'effort': 900} |   {'effort': 900} | ||||||
| 
 | 
 | ||||||
|  | Export of work data | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  |   >>> from loops.organize.work.report import WorkStatementCSVExport | ||||||
|  |   >>> reportView = WorkStatementCSVExport(task01, TestRequest()) | ||||||
|  |   >>> reportView.nodeView = nodeView | ||||||
|  | 
 | ||||||
|  |   >>> output = reportView() | ||||||
|  |   >>> print output | ||||||
|  |   Day;Start;End;Task;Party;Title;Duration;Effort;State | ||||||
|  |   08/12/28;19:00;20:15;loops Development;john;;1.25;0.25;finished | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| Meeting Minutes | Meeting Minutes | ||||||
| =============== | =============== | ||||||
|  |  | ||||||
|  | @ -101,6 +101,12 @@ | ||||||
|       class="loops.organize.work.report.WorkStatementView" |       class="loops.organize.work.report.WorkStatementView" | ||||||
|       permission="zope.View" /> |       permission="zope.View" /> | ||||||
| 
 | 
 | ||||||
|  |   <browser:page | ||||||
|  |       name="work.csv" | ||||||
|  |       for="loops.organize.interfaces.IConceptSchema" | ||||||
|  |       class="loops.organize.work.report.WorkStatementCSVExport" | ||||||
|  |       permission="zope.View" /> | ||||||
|  | 
 | ||||||
|   <zope:adapter  |   <zope:adapter  | ||||||
|       name="meeting_minutes" |       name="meeting_minutes" | ||||||
|       factory="loops.organize.work.report.MeetingMinutes" |       factory="loops.organize.work.report.MeetingMinutes" | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ from cybertools.organize.interfaces import IWorkItems | ||||||
| from cybertools.util.date import timeStamp2Date, timeStamp2ISO | from cybertools.util.date import timeStamp2Date, timeStamp2ISO | ||||||
| from cybertools.util.jeep import Jeep | from cybertools.util.jeep import Jeep | ||||||
| from loops.common import adapted, baseObject | from loops.common import adapted, baseObject | ||||||
|  | from loops.expert.browser.export import ReportConceptCSVExport | ||||||
| from loops.expert.browser.report import ReportConceptView | from loops.expert.browser.report import ReportConceptView | ||||||
| from loops.expert.field import Field, TargetField, DateField, StateField, \ | from loops.expert.field import Field, TargetField, DateField, StateField, \ | ||||||
|                             TextField, HtmlTextField, UrlField |                             TextField, HtmlTextField, UrlField | ||||||
|  | @ -49,6 +50,11 @@ class WorkStatementView(ReportConceptView): | ||||||
|     reportName = 'work_statement' |     reportName = 'work_statement' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class WorkStatementCSVExport(ReportConceptCSVExport): | ||||||
|  | 
 | ||||||
|  |     reportName = 'work_statement' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # fields | # fields | ||||||
| 
 | 
 | ||||||
| class DurationField(Field): | class DurationField(Field): | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue