[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 5a453a0 1/2: Embed {{MST}} and special markup
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 5a453a0 1/2: Embed {{MST}} and special markup in product database |
Date: |
Thu, 8 Aug 2019 20:53:58 -0400 (EDT) |
branch: master
commit 5a453a0d07de00cbda6b4f8f2ea9f66b696e2029
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Embed {{MST}} and special markup in product database
Motivating example:
"<br><b>Definition #1:</b><br>{{phrase_1a}} some words {{phrase2a}}."
For instance, product_data::GuarMortalityFootnote says essentially:
"This [policy|contract|certificate] has guaranteed COIs
based on the [1980|2001|2017] CSO table"
and at present the database of proprietary products has distinct hard-
coded strings for all possibilities:
guar_mortality_footnote_1980
...
guar_mortality_certificate_footnote_2001
...
guar_mortality_policy_footnote_2017
...
one of which is specified for each of about 140 products. That's
redundant because the product database already has variables that hold
the variant components like "certificate" and "2017":
product_data::ContractName --> LedgerInvariant::ContractName
DBDictionary::CsoEra [to be mapped to a LedgerInvariant member soon]
The goal is to have a single "GuarMortalityFootnote" string for all
products, with the variant parts filled in automatically. There are
about a hundred footnote strings like this, and the evolving practice
(at least for one insurer) is to use exactly the same terminology as the
corresponding policy form, instead of generic wording like:
"This product has guaranteed COIs based on the applicable CSO table"
Often such "footnotes" embody definitions of terms such as
AttainedAgeFootnote = "The [insured|annuitant]'s age at the end of
the illustrated [policy|contract|certificate] year."
and the evolving practice is to format that thus:
<b>End of Year Age:</b> {{AttainedAgeFootnote}}
In theory, such a string could be hard-coded OAOO in each '.mst' file.
In practice, that approach has grown unworkable. For example, suppose
that a PDF resulting from
A.policy + A.database + X.mst --> AX.pdf
is formally filed with regulators; later, when a different product B
B.policy + B.database + X.mst --> BX.pdf
is to be filed, an arbitrary difference such as
- <b>End of Year Age:</b> {{AttainedAgeFootnote}}
+ <b>Age at End of Year:</b> {{AttainedAgeFootnote}}
may be considered necessary, simply because B's contract defined a
trivially different term for the same concept; but modifying template
X.mst potentially changes the already-filed AX.pdf, which is forbidden.
Virtualizing the name:
<b>{{EndOfYearAgeTerminology}}</b> {{AttainedAgeFootnote}}
might seem like a good idea, but really isn't, because that's just one
of an infinitude of possible variations, e.g.:
"...at the end of the illustrated [policy|contract|certificate] year."
"...at the end of the depicted [policy|contract|certificate] year."
which might be demanded for "business reasons".
Instead, ideally, footnotes could be consolidated into a handful of
multiple-paragraph blurbs:
std::string blurb1 = "term0: {{defn0}}<br>term1:{{defn1}}<br>...";
which would be held in '.policy' files, with html markup and mustache
substitutions realized when a PDF is generated. IOW, the vision is to
change the contents of '.policy' files from flat text to something like
mustache "partials" (even if the {{>partial}} syntax isn't used).
Special markup is used instead of html; the "motivating example" above
becomes
- "<br><b>Definition #1:</b><br>{{phrase_1a}} some words {{phrase2a}}."
+ "¶«Definition #1:»¶{{phrase_1a}} some words {{phrase2a}}."
Rationale: allowing html as such would create too great a temptation to
use more and more exotic html features; defining special meanings only
for pilcrows and guillemets restricts markup creep.
While this commit does seem to be correct, it should be refactored when
convenient. The special-markup code uses std::regex, so it's terse, but
probably slow; and calling interpolate_string() twice can't be optimal.
---
pdf_command_wx.cpp | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/pdf_command_wx.cpp b/pdf_command_wx.cpp
index c43a359..bfbfb10 100644
--- a/pdf_command_wx.cpp
+++ b/pdf_command_wx.cpp
@@ -58,6 +58,7 @@
#include <fstream>
#include <map>
#include <memory> // make_unique(), unique_ptr
+#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
@@ -143,6 +144,20 @@ class html_interpolator
throw std::runtime_error("invalid lookup kind");
}
+ static std::string reprocess(std::string const& raw_text)
+ {
+ std::string z = raw_text;
+
+ z = std::regex_replace(z, std::regex("¶"), "<br>");
+ z = std::regex_replace(z, std::regex("«"), "<strong>");
+ z = std::regex_replace(z, std::regex("»"), "</strong>");
+
+ std::regex const empty_paragraph("< *[Pp] *>[[:space:]]*< */[Pp] *>");
+ z = std::regex_replace(z, empty_paragraph, "");
+
+ return z;
+ }
+
// A function which can be used to interpolate an HTML string containing
// references to the variables defined for this illustration. The general
// syntax is the same as in the global interpolate_string() function, i.e.
@@ -156,9 +171,21 @@ class html_interpolator
// variables explicitly defined by add_variable() calls.
html::text operator()(char const* s) const
{
+ std::string z =
+ interpolate_string
+ (s
+ ,[this]
+ (std::string const& str
+ ,interpolate_lookup_kind kind
+ )
+ {
+ return interpolation_func(str, kind);
+ }
+ )
+ ;
return html::text::from_html
(interpolate_string
- (s
+ (reprocess(z).c_str()
,[this]
(std::string const& str
,interpolate_lookup_kind kind