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.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)

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 # 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'

View file

@ -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

View file

@ -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
=============== ===============

View file

@ -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"

View file

@ -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):