provide CSV download view for reports; use for work and qualification listings

This commit is contained in:
Helmut Merz 2015-03-13 14:27:30 +01:00
parent 1716990d26
commit e16f3e5ccf
7 changed files with 134 additions and 2 deletions

77
expert/browser/export.py Normal file
View 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))

View file

@ -22,6 +22,7 @@ Field definitions for reports.
from zope.app.form.browser.interfaces import ITerms
from zope import component
from zope.i18n import translate
from zope.i18n.locales import locales
from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder
@ -92,6 +93,11 @@ class DateField(Field):
format = ('date', 'short')
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):
value = self.getRawValue(row)
if not value:
@ -132,6 +138,15 @@ class WorkItemStateField(Field):
statesDefinition = 'workItemStates'
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):
if row.context is None:
return None
@ -241,14 +256,18 @@ class TrackDateField(Field):
cssClass = 'right'
def getValue(self, row):
reportMode = getattr(row.parent.context.view, 'reportMode', None)
if reportMode == 'export':
return self.getDisplayValue(row)
value = self.getRawValue(row)
if not value:
return None
return timeStamp2Date(value)
def getDisplayValue(self, row):
value = self.getValue(row)
value = self.getRawValue(row)
if value:
value = timeStamp2Date(value)
view = row.parent.context.view
return formatDate(value, self.part, self.format,
view.languageInfo.language)

View file

@ -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
# 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 loops.browser.concept import ConceptView
from loops.expert.browser.export import ReportConceptCSVExport
from loops.expert.browser.report import ResultsConceptView
from loops.organize.party import getPersonForUser
from loops.util import _
@ -35,3 +36,8 @@ class Qualifications(ResultsConceptView):
reportName = 'qualification_overview'
class QualificationsCSVExport(ReportConceptCSVExport):
reportName = 'qualification_overview'

View file

@ -23,6 +23,12 @@
factory="loops.knowledge.qualification.browser.Qualifications"
permission="zope.View" />
<browser:page
name="qualifications.csv"
for="loops.organize.interfaces.IConceptSchema"
class="loops.knowledge.qualification.browser.QualificationsCSVExport"
permission="zope.View" />
<!-- reports -->
<zope:adapter

View file

@ -234,6 +234,18 @@ The user interface is a ReportConceptView subclass that is directly associated w
>>> results.totals.data
{'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
===============

View file

@ -101,6 +101,12 @@
class="loops.organize.work.report.WorkStatementView"
permission="zope.View" />
<browser:page
name="work.csv"
for="loops.organize.interfaces.IConceptSchema"
class="loops.organize.work.report.WorkStatementCSVExport"
permission="zope.View" />
<zope:adapter
name="meeting_minutes"
factory="loops.organize.work.report.MeetingMinutes"

View file

@ -32,6 +32,7 @@ from cybertools.organize.interfaces import IWorkItems
from cybertools.util.date import timeStamp2Date, timeStamp2ISO
from cybertools.util.jeep import Jeep
from loops.common import adapted, baseObject
from loops.expert.browser.export import ReportConceptCSVExport
from loops.expert.browser.report import ReportConceptView
from loops.expert.field import Field, TargetField, DateField, StateField, \
TextField, HtmlTextField, UrlField
@ -49,6 +50,11 @@ class WorkStatementView(ReportConceptView):
reportName = 'work_statement'
class WorkStatementCSVExport(ReportConceptCSVExport):
reportName = 'work_statement'
# fields
class DurationField(Field):