[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
gnue/reports AUTHORS FAQ INSTALL samples/foobul...
From: |
Jason Cater |
Subject: |
gnue/reports AUTHORS FAQ INSTALL samples/foobul... |
Date: |
Tue, 09 Apr 2002 18:55:41 -0400 |
CVSROOT: /cvsroot/gnue
Module name: gnue
Changes by: Jason Cater <address@hidden> 02/04/09 18:55:41
Modified files:
reports : AUTHORS FAQ INSTALL
reports/samples/foobulations: monthly.grd
reports/src : GRDataMapper.py GRLayout.py GRParser.py
GRRun.py
Added files:
reports/filters: README
Log message:
lots of work on summaries; misc cleanup
CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/AUTHORS.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/FAQ.diff?tr1=1.1&tr2=1.2&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/INSTALL.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/filters/README?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/samples/foobulations/monthly.grd.diff?tr1=1.11&tr2=1.12&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/src/GRDataMapper.py.diff?tr1=1.11&tr2=1.12&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/src/GRLayout.py.diff?tr1=1.25&tr2=1.26&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/src/GRParser.py.diff?tr1=1.21&tr2=1.22&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue/reports/src/GRRun.py.diff?tr1=1.7&tr2=1.8&r1=text&r2=text
Patches:
Index: gnue/reports/AUTHORS
diff -c gnue/reports/AUTHORS:1.3 gnue/reports/AUTHORS:1.4
*** gnue/reports/AUTHORS:1.3 Sun Oct 7 02:28:34 2001
--- gnue/reports/AUTHORS Tue Apr 9 18:55:41 2002
***************
*** 1,2 ****
--- 1,5 ----
Jason Cater <address@hidden>:
Initial coding
+
+ Derek Neighbors <address@hidden>:
+ XSL transformation scripts
Index: gnue/reports/FAQ
diff -c gnue/reports/FAQ:1.1 gnue/reports/FAQ:1.2
*** gnue/reports/FAQ:1.1 Wed Apr 11 13:08:32 2001
--- gnue/reports/FAQ Tue Apr 9 18:55:41 2002
***************
*** 0 ****
--- 1,9 ----
+
+ Q: Why do you use XSLT? Isn't it slow?
+
+ A: XSLT is a means to an end. Currently, it provides the biggest bang
+ for the buck. Reports will have hooks so that "formatting engines"
+ can be written that can output directly to the desired output.
+ However, there are more pressing issues than such engines and XSLT
+ serves our needs well enough for the foreseeable future.
+
Index: gnue/reports/INSTALL
diff -c gnue/reports/INSTALL:1.3 gnue/reports/INSTALL:1.4
*** gnue/reports/INSTALL:1.3 Sun Oct 7 02:28:34 2001
--- gnue/reports/INSTALL Tue Apr 9 18:55:41 2002
***************
*** 25,36 ****
The following minimum programs are required for running GNUe-Reports:
Python 2.0 or later http://www.python.org/
! PyXML http://pyxml.sourceforge.net/
! GNUe-Common http://www.gnue.org/
!
! To do any useful report conversions, you may need any of the following:
! * A TeX formatting engine (e.g., for Linux, TeTeX)
! * An XSLT processor (see Sablotron at ????)
--- 25,33 ----
The following minimum programs are required for running GNUe-Reports:
Python 2.0 or later http://www.python.org/
! GNUe-Common http://www.gnuenterprise.org/downloads/common
! To do any useful report conversions based on our samples, you will need:
! * An XSLT processor (currently we use Sablotron and PySablot)
Index: gnue/reports/samples/foobulations/monthly.grd
diff -c gnue/reports/samples/foobulations/monthly.grd:1.11
gnue/reports/samples/foobulations/monthly.grd:1.12
*** gnue/reports/samples/foobulations/monthly.grd:1.11 Tue Apr 9 02:22:57 2002
--- gnue/reports/samples/foobulations/monthly.grd Tue Apr 9 18:55:41 2002
***************
*** 28,34 ****
</out:tablehead>
<section source="dtsFoo" name="nameBreak">
! <section>
<out:row>
<firstRow><out:col><field name="name"
section="nameBreak"/></out:col></firstRow>
<notFirstRow><out:col/></notFirstRow>
--- 28,34 ----
</out:tablehead>
<section source="dtsFoo" name="nameBreak">
! <section name="details">
<out:row>
<firstRow><out:col><field name="name"
section="nameBreak"/></out:col></firstRow>
<notFirstRow><out:col/></notFirstRow>
***************
*** 39,45 ****
<out:row type="subtotal" level="2"> <!-- TODO: Replace w/summs
once they work -->
<out:col/>
<out:col align="right">Total for <field name="name"/></out:col>
! <out:col align="right">12<!--summ function="sum"
section="nameBreak" field="foobs"/--></out:col>
</out:row>
<default>
<out:row>
--- 39,45 ----
<out:row type="subtotal" level="2"> <!-- TODO: Replace w/summs
once they work -->
<out:col/>
<out:col align="right">Total for <field name="name"/></out:col>
! <out:col align="right"><summ function="sum" section="details"
field="foobs"/></out:col>
</out:row>
<default>
<out:row>
***************
*** 52,58 ****
<out:row type="subtotal" level="1">
<out:col/>
<out:col align="right">Grand Total</out:col>
! <out:col align="right">36</out:col>
</out:row>
--- 52,58 ----
<out:row type="subtotal" level="1">
<out:col/>
<out:col align="right">Grand Total</out:col>
! <out:col align="right"><summ function="sum" section="nameBreak"
field="foobs"/></out:col>
</out:row>
Index: gnue/reports/src/GRDataMapper.py
diff -c gnue/reports/src/GRDataMapper.py:1.11
gnue/reports/src/GRDataMapper.py:1.12
*** gnue/reports/src/GRDataMapper.py:1.11 Mon Apr 8 12:03:20 2002
--- gnue/reports/src/GRDataMapper.py Tue Apr 9 18:55:41 2002
***************
*** 1,19 ****
#
# This file is part of GNU Enterprise.
#
! # GNU Enterprise 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, or (at your option) any later version.
#
! # GNU Enterprise 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 program; see the file COPYING. If not,
! # write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright 2000-2002 Free Software Foundation
--- 1,19 ----
#
# This file is part of GNU Enterprise.
#
! # GNU Enterprise 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, or (at your option) any later version.
#
! # GNU Enterprise 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 program; see the file COPYING. If not,
! # write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright 2000-2002 Free Software Foundation
***************
*** 32,43 ****
--- 32,50 ----
import GRExceptions
from gnue.common import GDebug
from gnue.common import GDataFormatter
+ import types, string
+
#
# Class used internally by GRDataMapper
# Classes other than GRDataMapper should not
# even care that this exists...
#
+ # There is one instance of this for EACH GRSection
+ #
+ # TODO: should this functionality be moved to the
+ # TODO: GRSection class since there's a 1:1 relation?
+ #
class GRDataMapperSection:
def __init__(self, name, source, parent):
self.parent = parent
***************
*** 51,58 ****
--- 58,72 ----
self.initial = 1 # Are we in an initial run in which we have no
history?
self.changed = 0 # Did this section change w/the last
self.grouping = 0 # Is this a "grouping" section?
+ self._lastSection = None
self.datasource = None
+ self._summMap = {'sum': self._summ_sum,
+ 'count': self._summ_count,
+ 'avg': self._summ_avg,
+ 'min': self._summ_min,
+ 'max': self._summ_max }
+
if parent != None:
parent.addChildSection(self)
***************
*** 82,92 ****
#
# Add a summary
#
! def addSummary(self, name, function):
! if not self.summaries.has_key(name):
! self.summaries[name] = {function:0}
! else:
! self.summaries[name][function] = 0
#
# Get a field's current value
--- 96,117 ----
#
# Add a summary
#
! def addSummary(self, function, field):
! try:
! self.summaries[field][function] = None
! except KeyError:
! self.summaries[field] = {function:None}
!
! # If this function relies on other
! # functions, add those as well (e.g.,
! # average depends on sum and count)
! try:
! for func in _summMultiMapping[function]:
! self.addSummary(self, func, field)
! except KeyError:
! pass
!
!
#
# Get a field's current value
***************
*** 98,104 ****
# Get a summary's current value
#
def getSummary(self, name, function, format):
! return self.summaries[name][function]
#
# Add a section
--- 123,133 ----
# Get a summary's current value
#
def getSummary(self, name, function, format):
! val = self.summaries[name][function]
! if val is None:
! return ""
! else:
! return "%s" % val
#
# Add a section
***************
*** 113,122 ****
self.initial = 1
self.changed = 0
#
# Save current values before new query occurs
#
! def _loadFields(self, recordset):
self.changed = 0
for field in self.fields.keys():
val = recordset.getField(field)
--- 142,164 ----
self.initial = 1
self.changed = 0
+
+ #
+ #
+ #
+ def clearSummaries(self):
+ print "clearing summaries for %s" % self.name
+ print self.summaries
+ for field in self.summaries.keys():
+ for function in self.summaries[field].keys():
+ self.summaries[field][function] = None
+
+
#
# Save current values before new query occurs
#
! def _loadFields(self, recordset, reset=0):
! if reset: self.clearSummaries()
self.changed = 0
for field in self.fields.keys():
val = recordset.getField(field)
***************
*** 127,135 ****
--- 169,195 ----
self.changed = 1
GDebug.printMesg(10, "Field %s changed after nextRecord
(%s,%s,%s,%s)" % (field, self.initial, self.grouping, self.fields[field], val))
self.fields[field] = val
+
+ ## handler = self
+ ## while handler:
+ ## handler._handleSummary(field, val)
+ ## handler = handler.parent
+
GDebug.printMesg(10, "After _loadFields, changed=%s" % self.changed)
self.initial = 0
+
+ #
+ # Initiate the calculation of summaries
+ #
+ def _handleSummary(self):
+ # Now, calc all summaries
+ for field in self.summaries.keys():
+ ## if self.summaries.has_key(field):
+ for function in _summFunctions:
+ if self.summaries[field].has_key(function):
+ self._summMap[function](field, self.fields[field])
+
#
# Will the next recordset cause
#
***************
*** 158,163 ****
--- 218,269 ----
self.resultset = detailResultSet
+ ##
+ ##
+ ##
+
+ # Summary function: "count"
+ def _summ_count(self, field, value):
+ if value is not None:
+ try:
+ self.summaries[field]['count'] += 1
+ except TypeError:
+ self.summaries[field]['count'] = 1
+
+ # Summary function: "sum"
+ def _summ_sum(self, field, value):
+ if type(value) in _numericTypes:
+ try:
+ self.summaries[field]['sum'] += value
+ except TypeError:
+ self.summaries[field]['sum'] = value
+ else:
+ raise "Attempting to 'sum' a non-numeric field %s" % (field)
+
+ # Summary function: "average"
+ def _summ_avg(self, field, value):
+ if type(value) in _numericTypes:
+ self.summaries[field]['average'] = \
+ self.summaries[field]['sum'] / self.summaries[field]['count']
+ else:
+ raise "Attempting to 'average' a non-numeric field %s" % (field)
+
+ # Summary function: "min"
+ def _summ_min(self, field, value):
+ if value is not None:
+ existing = self.summaries[field]['min']
+ if existing is None or value < existing:
+ self.summaries[field]['min'] = value
+
+ # Summary function: "max"
+ def _summ_max(self, field, value):
+ if value is not None:
+ existing = self.summaries[field]['min']
+ if existing is None or value > existing:
+ self.summaries[field]['min'] = value
+
+
+
###########################################
#
#
***************
*** 209,216 ****
self.sources.getDataSource(self.sectionMap[section].source) \
.referenceField(field)
! def addSummaryToSection(self, section, field, function):
! pass # This should probably be replaced with real code :)
#
--- 315,326 ----
self.sources.getDataSource(self.sectionMap[section].source) \
.referenceField(field)
!
! def addSummaryToSection(self, function, section, field):
!
! self.sectionMap[section].addSummary(function, field)
! self.sources.getDataSource(self.sectionMap[section].source) \
! .referenceField(field)
#
***************
*** 258,264 ****
if not controlSection.__nextRecord:
return (None, None)
else:
!
recordset = controlSection.__nextRecord
if controlSection.resultset.nextRecord():
controlSection.__nextRecord = controlSection.resultset.current
--- 368,374 ----
if not controlSection.__nextRecord:
return (None, None)
else:
!
recordset = controlSection.__nextRecord
if controlSection.resultset.nextRecord():
controlSection.__nextRecord = controlSection.resultset.current
***************
*** 282,289 ****
--- 392,421 ----
s._precheckNextFields(controlSection.__nextRecord)):
nextSection = s.name
+ if nextSection and nextSection != s._lastSection:
+ s._clearSummary()
+ s._handleSummary()
+
+ s._lastSection = firstSection
return (firstSection, nextSection)
+ #
+ # Contains all valid "summary" functions
+ # and the order they need to be calculated
+ # (e.g., count and sum need to happen before
+ # average)
+
+ _summFunctions = ('count','sum','min','max','avg','_accum',)
+ #
+ # Contains all "summary" functions that
+ # depend on other record keeping. (e.g.,
+ # 'averages' need the total and the count
+ # tracked.
+ #
+ _summMultiMapping = { 'avg': ('sum','count'),
+ }
+ # Python types that are numeric
+ _numericTypes = (types.FloatType, types.IntType, types.LongType)
Index: gnue/reports/src/GRLayout.py
diff -c gnue/reports/src/GRLayout.py:1.25 gnue/reports/src/GRLayout.py:1.26
*** gnue/reports/src/GRLayout.py:1.25 Tue Apr 9 01:00:35 2002
--- gnue/reports/src/GRLayout.py Tue Apr 9 18:55:41 2002
***************
*** 115,121 ****
elif isinstance(object, GRField):
s = parentSection.getAncestorWithSource(object._source)
if s == None:
! raise SourceOutOfScope, \
'Field "%s" in section "%s" uses out-of-scope source "%s"' \
% (object.name, parentSection._name, object._source)
--- 115,121 ----
elif isinstance(object, GRField):
s = parentSection.getAncestorWithSource(object._source)
if s == None:
! raise GRExceptions.SourceOutOfScope, \
'Field "%s" in section "%s" uses out-of-scope source "%s"' \
% (object.name, parentSection._name, object._source)
***************
*** 132,138 ****
curr = s._parent.findParentOfType('GRSection')
if section == None:
! raise SourceOutOfScope, \
'Field "%s" in section "%s" uses out-of-scope section "%s"' \
% (object.name, parentSection._name, object.section)
--- 132,138 ----
curr = s._parent.findParentOfType('GRSection')
if section == None:
! raise GRExceptions.SourceOutOfScope, \
'Field "%s" in section "%s" uses out-of-scope section "%s"' \
% (object.name, parentSection._name, object.section)
***************
*** 145,172 ****
self._mapper.addFieldToSection(object._section, object.name)
elif isinstance(object, GRSumm):
- sec = string.lower(object.section)
if object.section == None:
! s =
object._parent.findObjectOfType('GRSection').getAncestorWithSource(object._source)
! if s == None:
! raise SourceOutOfScope, \
! 'Summary "%s" in section "%s" uses out-of-scope source "%s"' \
! % (object.name, object._parent.name, object._source)
else:
! s = self._mapper.sectionMap[sec]._object.\
! getAncestorWithSource(object._source)
! if s == None:
! raise SourceOutOfScope, \
! 'Summary "%s" in section "%s" uses out-of-scope source "%s"' \
! % (object.name, object._parent.name, object._source)
object._section = s._name
! object._mymapper = self._mapper.sectionMap[object._section]
GDebug.printMesg(6,'Mapping summary %s [%s] to section %s' \
! % (object.name, object.function, object._section))
! self._mapper.addSummaryToSection(object._section, object.name, \
! object.function)
#
--- 145,167 ----
self._mapper.addFieldToSection(object._section, object.name)
elif isinstance(object, GRSumm):
if object.section == None:
! s = object._parent.findObjectOfType('GRSection')
else:
! sec = string.lower(object.section)
! s = self._mapper.sectionMap[sec]._object
! if s == None:
! raise GRExceptions.SourceOutOfScope, \
! 'Summary "%s" in section "%s" uses out-of-scope source "%s"' \
! % (object._field, object._parent.name, object._source)
object._section = s._name
! object._mymapper = self._mapper.sectionMap[s._name]
GDebug.printMesg(6,'Mapping summary %s [%s] to section %s' \
! % (object._field, object.function, object._section))
! self._mapper.addSummaryToSection(object.function,
! object._section, object._field)
#
***************
*** 237,246 ****
--- 232,249 ----
if child._type == "_content_":
dest.write(child.getContent())
else:
+ # Handle GRSections specially as
+ # they require extra logic
if isinstance(child, GRSection):
+
+ # If this is the top-most section for a datasource,
+ # it needs to be called with processAsController.
if child._mymapper.toplevel:
GDebug.printMesg(10, "Calling new controlling section")
+
child.processAsController(dest, mapper)
+
+ # ..otherwise call the GRSection's process method.
else:
nextSection = child.process(dest, mapper,
isfirst=1,
***************
*** 250,255 ****
--- 253,259 ----
nextSection=nextSection)
else:
+ # Handle the layout element.
nextSection = child.process(dest, mapper, first, islast,
firstSection, nextSection)
***************
*** 316,327 ****
self.process(dest, mapper, 1, 1, None, None)
else:
- if mapper.getFirstRecord(self._source):
- # This is a data-bound section and there
- # was data returned, so process section
! firstSection, nextSection = self.getGotos(mapper,1)
self.process(dest, mapper, 1, nextSection == None, self, nextSection)
else:
--- 320,331 ----
self.process(dest, mapper, 1, 1, None, None)
else:
! firstSection, nextSection = self.getGotos(mapper, 1)
+ if firstSection:
+ # This is a data-bound section and there
+ # was data returned, so process section
self.process(dest, mapper, 1, nextSection == None, self, nextSection)
else:
***************
*** 337,346 ****
#
# Used internally to determine next section to move
#
! def getGotos(self, mapper, first=0):
# Determine the next section to process our data.
# (i.e., handle section grouping)
! if first:
goto, gotoNext = mapper.getFirstRecord(self._source)
else:
goto, gotoNext = mapper.getNextRecord(self._source)
--- 341,350 ----
#
# Used internally to determine next section to move
#
! def getGotos(self, mapper, init=0):
# Determine the next section to process our data.
# (i.e., handle section grouping)
! if init:
goto, gotoNext = mapper.getFirstRecord(self._source)
else:
goto, gotoNext = mapper.getNextRecord(self._source)
***************
*** 440,450 ****
self._section = string.lower(self.section)
else:
self._section = None
return GRLayoutElement._buildObject(self)
def process(self, dest, mapper, isfirst, islast, firstSection, nextSection):
structuralComment(dest,"<!--[summ:%s]-->" % self.name)
! dest.write (self._mymapper.getSummary(self.name, self.function,
self.format))
structuralComment(dest,"<!--[/summ:%s]-->" % self.name)
return nextSection
--- 444,456 ----
self._section = string.lower(self.section)
else:
self._section = None
+
+ self._field = string.lower(self.field)
return GRLayoutElement._buildObject(self)
def process(self, dest, mapper, isfirst, islast, firstSection, nextSection):
structuralComment(dest,"<!--[summ:%s]-->" % self.name)
! dest.write (self._mymapper.getSummary(self._field, self.function,
self.format))
structuralComment(dest,"<!--[/summ:%s]-->" % self.name)
return nextSection
Index: gnue/reports/src/GRParser.py
diff -c gnue/reports/src/GRParser.py:1.21 gnue/reports/src/GRParser.py:1.22
*** gnue/reports/src/GRParser.py:1.21 Tue Apr 9 01:00:35 2002
--- gnue/reports/src/GRParser.py Tue Apr 9 18:55:41 2002
***************
*** 22,28 ****
# GRParser.py
#
# DESCRIPTION:
! # Class that contains a sax based xml processor for GNUe Reports
#
# NOTES:
#
--- 22,28 ----
# GRParser.py
#
# DESCRIPTION:
! # Class that contains a SAX-based xml processor for GNUe Reports
#
# NOTES:
#
Index: gnue/reports/src/GRRun.py
diff -c gnue/reports/src/GRRun.py:1.7 gnue/reports/src/GRRun.py:1.8
*** gnue/reports/src/GRRun.py:1.7 Sat Apr 6 20:20:36 2002
--- gnue/reports/src/GRRun.py Tue Apr 9 18:55:41 2002
***************
*** 63,73 ****
'and fax. Note that printer, email, and fax are sent via the '
'server\'s machine, not the client\'s machine. To '
'\nNOTE: Only file, printer, and email are currently implemented!'
],
! [ 'destination', 'p', 'destination', 1, '-', 'dest',
'Where should the report be output to? The value of this '
'depends on the destination type (e.g., if sending to printer, '
! 'then -p specifies the printer name; if sending via email, then '
! '-p specifies the email address.) If <dest> is "-", then output is '
'sent to stdout -- NOTE: when sending to stdout, also use the -q '
'[--quiet] option or you may get junk in your output stream. '
'NOTE: Currently the default value is "-" -- this may change once
GNUe '
--- 63,73 ----
'and fax. Note that printer, email, and fax are sent via the '
'server\'s machine, not the client\'s machine. To '
'\nNOTE: Only file, printer, and email are currently implemented!'
],
! [ 'destination', 'd', 'destination', 1, '-', 'dest',
'Where should the report be output to? The value of this '
'depends on the destination type (e.g., if sending to printer, '
! 'then -d specifies the printer name; if sending via email, then '
! '-d specifies the email address.) If <dest> is "-", then output is '
'sent to stdout -- NOTE: when sending to stdout, also use the -q '
'[--quiet] option or you may get junk in your output stream. '
'NOTE: Currently the default value is "-" -- this may change once
GNUe '
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- gnue/reports AUTHORS FAQ INSTALL samples/foobul...,
Jason Cater <=