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