[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [lmi] calculation summary, questions about xslt
From: |
Greg Chicares |
Subject: |
Re: [lmi] calculation summary, questions about xslt |
Date: |
Thu, 12 Oct 2006 13:40:09 +0000 |
User-agent: |
Thunderbird 1.5.0.4 (Windows/20060516) |
On 2006-10-12 11:44 UTC, Evgeniy Tarassov wrote:
> On 10/10/06, Greg Chicares <address@hidden> wrote:
>> On 2006-10-10 0:23 UTC, Evgeniy Tarassov wrote:
>> >
>> > 2) html.xsl
>> >
>> > 2.1 the code generating html contains an expression
>> >
>> > (is_subject_to_ill_reg(ledger_values.GetLedgerType()))
[...]
> What do you think could be the
> best name for that value? Maybe something like:
>
> <double_scalar name="SubjectToIllReg">1</double_scalar>
> with its value taking 1 or 0 and numeric-formatting rule 'F1' applied.
Could we use
name="IsSubjectToIllustrationReg"
? That would exactly match the name of this function:
inline bool BasicValues::IsSubjectToIllustrationReg() const
{
return IsSubjectToIllustrationReg_;
}
and make grepping easier.
[Grepping isn't effortless due to conflicting naming conventions,
e.g.,
bool is_subject_to_ill_reg(e_ledger_type const&);
but I'm gradually trying to resolve name conflicts like that,
generally preferring PascalCaseWithFirstWordCapitalized.]
BTW, this entity is boolean by nature. It's a double in C++ only
because C++ doesn't handle arrays of heterogenous type well.
Several other entities are boolean:
double IsMec;
double InforceIsMec;
double IsInforce;
double PremiumTaxLoadIsTiered;
and I suppose it's possible to validate them as such, but does
that really make sense? There are others like
double LapseMonth; // Constrained to [0-11] IIRC.
and I hesitate to express extensive "type" knowledge in xslt
because it sounds like a lot of work. But if you think it's a
good idea to validate the "contract" on the xslt side, then
maybe we should consider that later.
>> > 4) tab_delimited.xsl
>> > 4.1 the code generating CSV formatted data contains some code checking
>> > that e_run_curr_basis_sa_zero is present in
>> > ledger_values.GetRunBases().
>> >
>> > std::vector<e_run_basis> const& bases(ledger_values.GetRunBases());
>> > if
>> > ( bases.end()
>> > != std::find(bases.begin(), bases.end(), e_run_curr_basis_sa_zero)
>> > )
>> > {
>> >
>> > Do you think there is already some way to check that using the already
>> > exported values?
[...]
> If the only purpose of that if statement is to keep code from calling some
> non-existing value,
That's right: print zero if it doesn't exist.
> then in xslt we could probably completly remove
> that. In xslt we can check if a particular value (in pur case
> @name='NetCOICharge', @basis='run_curr_basis_sa_zero') is present and
> then output its value, or place a '0', if the value is not present.
Well, that sounds like what we want, but...when the value is
missing, it's really an arithmetic zero, not a literal '0'.
If this is one of several columns and all the others are all
formatted as "99,999.99", then this missing column would
ideally be shown as "0.00"--otherwise, it would look strange.
However, we decided to do formatting in C++, so we locked
ourselves out of this solution, at least for now.
But it's not actually a problem. This is a unique special case.
Usually it's inappropriate to print a missing value. Here, we
want to print every value, but only in a tab-delimited file
designed to preserve all possible precision so that the numbers
can be manipulated downstream, e.g., in a spreadsheet. Thus, the
format should be exactly what 'numeric_io_cast.hpp' does, and a
value of zero should always be formatted exactly as "0".
So the precise full answer to your question is "Yes".
>> if
>> ( bases.end()
>> != std::find(bases.begin(), bases.end(), e_run_curr_basis_sa_half)
>> )
>> {
>> fatal_error()
>>
>> This is really just an assertion. It seems appropriate to keep
>> that code, and run it whenever data is prepared for transmission
>> to xslt.
>
> This assertion is present in PrintFormTabDelimited but not in
> Ledger::write code. Does it mean that for that type of ledger we
> simply can not produce tab delimited text, or does it mean that entire
> calculation summary does not make sense for that type of ledger?
We've got at least three functions to print an object of class
'Ledger':
void Ledger::write(xml::node&) const;
This prints all meaningful, non-empty data.
std::string LMI_SO FormatSelectedValuesAsHtml
(Ledger const& ledger_values
);
This prints the calculation summary. A shortsighted approach
would have been to express only this function in xslt. I think
you've taken a sounder approach: use Ledger::write() to write
all the (non-empty) data, then use that data to produce various
kinds of reports--so that this C++ function won't exist anymore.
[But see important note below.]
void LMI_SO PrintFormTabDelimited
(Ledger const& ledger_values
,std::string const& file_name
);
This is the tab-delimited "spreadsheet" file. It has a unique
extra requirement: that empty data be shown as "0".
We could think of the first, Ledger::write(), as supplying raw
data for xslt, and the other two as particular reports generated
from that single set of raw data.
Now let me answer your questions in that context.
> This assertion is present in PrintFormTabDelimited but not in
> Ledger::write code.
That's the only place where it's needed in the C++ implementation.
PrintFormTabDelimited() needs to print even "missing" data.
But it doesn't print all conceivable "missing" data....
Consider the example you gave (farther) above:
@name='NetCOICharge', @basis='run_curr_basis_sa_zero'
in a situation where 'run_curr_basis_sa_zero' wouldn't be used:
it's "missing". In the xml file, there'd be nothing, and you
have a tidy way to print '0'. In C++, printing "nothing" would
cause a segfault, so we write conditional code to avoid the
segfault and print zero instead.
But your immediate question is about 'e_run_curr_basis_sa_half',
not 'run_curr_basis_sa_zero'. The 'e_run_curr_basis_sa_half'
basis is never used today, but was used in the past and might
be wanted again in the future. For PrintFormTabDelimited(),
we might have written the same sort of special-case code for
e_run_curr_basis_sa_half
as for
run_curr_basis_sa_zero
and printed zeros. But 'e_run_curr_basis_sa_half' is very
special--it arose only to support a regulation that was later
repealed--and users don't really want it in the "spreadsheet"
report produced by PrintFormTabDelimited(), so we have this
different special-case treatment.
For xslt, you can just pretend that 'e_run_curr_basis_sa_half'
doesn't exist. There's a chance that we'll want to add it in
the future, but the probability seems low.
> Does it mean that for that type of ledger we
> simply can not produce tab delimited text, or does it mean that entire
> calculation summary does not make sense for that type of ledger?
There's only one class 'Ledger', but the "bases" present in it do
vary. For xslt, assume that 'e_run_curr_basis_sa_half' is never
present--it's a "basis" xslt doesn't need to know about.
Ideally, in the long term, we want to produce all reports from
the same dataset, and that dataset would be what
void Ledger::write(xml::node&) const;
creates.
Important note: For now at least, we decided to make that ideal
more difficult, in the name of expediency. The difficulty is
formatting. There were good reasons for keeping the formatting
in C++, at least for now. But suppose that this scalar
GuarPrem
has the value 1234.5678 and is formatted, for the calculation
summary, as "1234.57". We'd write "1234.57" to xml, and use
xslt to produce the calculation summary. The same xml file
wouldn't be suitable for the "spreadsheet", which needs all
possible precision: there, we'd want "1234.5678".
We can cross that bridge when we come to it. I see at least
two options: either (1) move the formatting to xslt (and
resolve all the potential issues previously identified), or
(2) produce a different xml file for each report we need for
any one ledger object.
Today, however, what we want to deliver in the short term is
only the calculation summary. We want to do that without
changing the "File | Print" output already produced by xslt
and fo, so the xml already produced by Ledger::write() for
that purpose can just be reused for the calculation summary.
Permitting the formats to vary by the type of report is what
we've deferred in order to deliver that more quickly.
>> // Show experience-rating columns for current-expense, zero-
>> // interest basis if used, to support testing.
>> std::vector<e_run_basis> const& bases(ledger_values.GetRunBases());
>> if
>> ( bases.end()
>> != std::find(bases.begin(), bases.end(), e_run_curr_basis_sa_zero)
>> )
>> {/* print the actual data */}
>> else
>> {/* print zeros */}
>>
>> Here, what's important is whether 'e_run_curr_basis_sa_zero' data
>> exists; if it didn't, there'd be a segfault. I understand C++ better
>> than I understand xslt, so I don't know how this would naturally be
>> handled in xslt; probably I'd better ask your advice.
>>
>> If the data for that "basis" fails to exist, we really do want to
>> print zeros. The goal is to use exactly the same column layout in
>
> Thank you for your explanation!
> Now if i understand it correctly we could rely on the presence of the
> value in the produced ledger data XML, since the values "protected" by
> the 'if' expression in PrintFormTabDelimited function will exist only
> when e_run_curr_basis_sa_zero is in ledger_values.GetRunBases(), and
> will _not_ be exported if e_run_curr_basis_sa_zero is _not_ in
> ledger_values.GetRunBases(). And we dont need to export any additional
> value into XML.
Yes, exactly. This kink is inherent in the problem domain.