lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Group premium quotes


From: Greg Chicares
Subject: Re: [lmi] Group premium quotes
Date: Fri, 19 Jun 2015 23:30:37 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Icedove/31.3.0

On 2015-06-19 14:20, Vadim Zeitlin wrote:
> On Wed, 17 Jun 2015 15:24:52 +0000 Greg Chicares <address@hidden> wrote:
> 
> GC> Right now, the main question from my POV at least is how to pass data
> GC> from lmi to the code that write the PDF file.
> GC> 
> GC>  - I could pass a struct. Then the code will look like different parts 
> were
> GC>    written by different people.
> 
>  I'm not sure why should it necessarily imply this.

Okay, it doesn't. It sounds like I was afraid we'd write in mutually
incomprehensible styles--as two C++ programmers separated by a common
language, which sounds funnier when Churchill says it. However, as
sometimes happens when I share a fuzzy idea because I don't have a
concrete one, what I really had in mind was something different:
that two parts, both developed before the whole can be conceived,
might turn out not to fit well.

IOW...what we might name the "Psychobilly Cadillac" anti-pattern,
after the song whose narrator, an auto worker, steals an entire car
"One Piece at a Time" by smuggling all the parts out in his lunchbox
over a period of decades:

The transmission was a fifty-three [GWC note: a 1953 model car]
And the motor turned out to be a seventy-three
And when we tried to put in the bolts all the holes were gone.

>  As for the "two pieces" part, for me this is more of an advantage than
> anything else as it would make each of them simpler and, for the PDF one,
> easier to test.

Very good point. The easiest way to regression-test the data that a
PDF contains is to dump the data as flat text, and test that.

> The main drawback I see with this approach is that it
> requires to decide in advance which data is passed from the GUI code to the
> PDF generation function and that if we decide wrongly, we would need to
> update the interface to allow passing more data.

I can almost see the dataset in its entirety.

The word "almost" is scary, though. We are seeking some clarifications.

>  This could be mitigated by using "generic" data structures such as passing
> the entire product_data (with all of its keys) and a self-describing 2D
> array for the premiums (i.e. an array containing information about its
> columns) and if we foresee the need for the information in the generated
> PDF to vary, then this would probably the best thing to do. But if we need
> to generate just the kind of summaries discussed so far, it would, IMHO, be
> better to avoid such broad stringly type interface and use a concrete
> struct.

Agreed. BTW, lmi's 'class ledger' is just such a "stringly" interface.
And I think that's a good design for its purpose: we can pass its data
through a variety of different XSL-FO transformations. In this case,
though, I think we'll want to dispense with that heavyweight "stringly"
intermediate class.

>  BTW, a somewhat related question: will the totals (for each column) be
> computed by lmi or PDF generation code? Computing the sum is not a
> difficult operation, as maths go, so I guess the PDF code could be trusted
> with it, or do you still see it as too risky?

It doesn't matter. Each premium in every column is already rounded to
cents. Adding them together will give substantially the same result
no matter where we do it.

> GC>  - The PDF code could call back into lmi for each datum where it's needed.
> GC>    That would probably work well for footnotes:
> GC>      product_data const& p = [whatever];
> GC>      std::string const marketing_name_footnote = 
> p.datum("MarketingNameFootnote");
> GC>      [then use that string in the PDF code]
> GC>    But it might not work so well for premiums. We certainly want to keep 
> the
> GC>    premium-calculation details encapsulated, as the PDF code has no need 
> to
> GC>    see how those calculations are performed. Maybe we need to create a
> GC>    quote_premiums object and pass that to the PDF code.
> 
>  The trouble is that I don't know where do these premiums live right now.
> Are they already available in some existing data structure?

Not all of them are even calculated now. I've been looking into the
pieces I need to use and thinking about how to tie this all together,
and have come to the conclusion that I should just take your specimen
PDF code and add my code to it. Then we can step back, take a look at
the resulting...prototype specimen, and it'll be much clearer whether
and where we should slice it.

On second though, let me sketch it out below...

>  If not, then I think we do need some object which would be part of the
> bigger struct describing the entire group premium document to generate.
> I.e. I see something like this, in pseudo-C++:
> 
>       struct premium_document_description
>       {
>           string
>               ,header_text
>               ,header_image_file

I want to postpone those two; we can hardcode something bland for now,
and later it will become clear what to do.

>               ,footer_text

I'm not sure what this would mean--I guess we have to get an example
that spans two pages to be sure.

>               ,company
>               ,prepared_by

Input::CorporationName and Input::AgentName, respectively. The
complication here is that, conceptually, there's
 - a top level, multiple_cell_document::case_parms_, which should
   represent input fields that are (again, conceptually) exactly
   the same for all participants; and
 - a detail level, multiple_cell_document::cell_parms_, at which
   every input field is actually present and modifiable (because
   that makes the system maximally flexible and powerful).
Here, the end users want the top-level inputs, but they don't
enter top-level data, and a custom GUI is out of the question
for this "premium quotes" task. I think the best solution is to
read these values from first cell (participant), and then assert
that they match in every other cell--assertion failure being an
error that prevents any PDF output from being generated.

>               ,summary_data

Input::Comments

Because this has to be explicitly entered somewhere, I think we
should prescribe multiple_cell_document::case_parms_ as the place
where it must be entered: ctrl-shift-E in lmi's GUI.

Speculatively, it might be handy to allow "templates" to be saved
as '.cns' files with typical summary_data strings...and then
modify CensusView::UponPasteCensus() so that saved "templates"
get the behavior under this condition:
    if(!document().IsModified() && !document().GetDocumentSaved())
because in practice end users will paste census data from some
file obtained from the client. Well, we can think about that
ease-of-use enhancement later.

>           struct participant

Now we're at the detail level, where the above complication vanishes.

>           {
>               string name;
>               int age;

Input::InsuredName, Input::IssueAge

>               double
>                   ,income
>                   ,face_amount

Input::ProjectedSalary, Input::SpecifiedAmount

Here's a complication that we must sidestep: these are input sequences,
but we aren't going to "realize" them by calling RealizeProjectedSalary()
or RealizeSpecifiedAmount(). In the one and only use case, these are
(string representations of) numeric scalars. Any other use constitutes
undefined behavior as defined in the language standard, with no diagnostic
required. If you enter "100000, 3; 50000" then we just copy that string to
output; or, if you like, we convert it to a numeric scalar, reformat it as
a string, and no tears if an exception gets thrown for unconvertible input.

As an alternative, we might call Input::RealizeAllSequenceInput() and then
construct a yare_input object and use that. I'll probably do that if it's
handy for the premium calculations. In that case, I guess we'd just select
the first element of each input sequence: yare_input::ProjectedSalary[0]
for example; optionally, we could assert that all elements of that
vector are equal.

>                   ,premium
>                   ,premium_with_waiver
>                   ,premium_with_adb
>                   ,premium_with_waiver_and_adb

I will need to calculate these explicitly; I haven't yet worked out exactly
how I want to do that.

>           vector<participant> participants;

This brings us back to the question whether, as we iterate through
multiple_cell_document::cell_parms_, we want to
 - populate such an intermediate structure, through which we'll later
     iterate to write PDF rows; or
 - write PDF rows as we process each cell_parms_ element.
This is discussed in more detail below.

> And then
> 
>       void create_premium_summary_pdf
>           (string const& file_name
>           ,premium_document_description const& data
>           );
> 
>  What do you think?

We'll need some driving function like this:

// argument might be std::string or boost::filesystem::path
// return value might be some struct
//
void foo(std::string census_filename)
{
    multiple_cell_document input(census_filename);

    // parse with xmlwrapp, then:
    std::vector<Input> const& cell_parms = document().doc_.cell_parms_;

    // Get case-level input
    std::string CorporationName = cell_parms_[0]["CorporationName"];
    for each iterator i in cell_parms_ do...
        assert(CorporationName == i->CorporationName);
    // similarly for AgentName

    // Calculate premiums
    for each iterator i in cell_parms_ do...
        prem_basic  = WriteThisFunction(*i);
        prem_WP     = WriteThisFunction(*i);
        prem_ADB    = WriteThisFunction(*i);
        prem_WP_ADB = WriteThisFunction(*i);
        // and then either:
        write_pdf_row(prem_basic, ...others..., i->InsuredName, ...others...);
        // or:
        intermediate_struct.vector<whatever>.push_back(same arguments);
        // and, whichever way we choose, also:
        if(testing) // or maybe just unconditionally, because it's cheap...
        // assume we've created a suitable std::ostringstream oss earlier...
        oss << all << that << data << above << as << text << std::endl;
}

Now we could organize the whole either this way:

void do_everything(...filename...)
{
   intermediate_struct z = foo(filename);
   // total every column somewhere...
   do_pdf_stuff(z);
}

or by intermingling foo() with PDF stuff in a single function. Is this
concrete enough for you to see which way we should do this so that we
don't wind up building a "Psychobilly Cadillac"?




reply via email to

[Prev in Thread] Current Thread [Next in Thread]