/libs/cgi/example/fcgi/amortization/main.cpp
C++ | 204 lines | 137 code | 32 blank | 35 comment | 6 complexity | 1a86786a3337472eb3da461b829b2b2a MD5 | raw file
- // -- main.hpp --
- //
- // Copyright (c) Darren Garvey 2007.
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- ////////////////////////////////////////////////////////////////
- //
- //[fcgi_amort
- //
- // Amortization Calculator
- // -----------------------
- //
- // This file uses Google cTemplate to show the benefits of using an
- // HTML template engine. The code isn't commented yet, but should be
- // *reasonably* self-explanatory.
- //
- // It is a very basic amortization calculator.
- //
- #include <iostream>
- #include <iomanip>
- #include <boost/cgi/fcgi.hpp>
- #include <boost/algorithm/string/regex.hpp>
- #include <ctemplate/template.h>
- using namespace boost::fcgi;
- /// Convert a string like '$250,000' into one like '250000'.
- std::string string_from_currency(std::string amt)
- {
- // this is much too hardcore, but it works fine...
- boost::algorithm::erase_all_regex(amt, boost::regex("[$, ]"));
- return amt;
- }
- /// This function fills a dictionary.
- template<typename Request>
- void fill_amortization_dictionary(
- ctemplate::TemplateDictionary& dict, Request& req)
- {
- dict.SetValue("LoanAmt", req.post.count("LoanAmt")
- ? "$250,000" : req.post["LoanAmt"]);
- dict.SetIntValue("YearlyIntRate", req.post.pick("YearlyIntRate", 6.000));
- boost::array<std::string, 8> year_opts
- = {{ "5", "7", "10", "20", "30", "40", "50" }};
-
- BOOST_FOREACH(std::string& year, year_opts)
- {
- dict.SetValueAndShowSection("TermYrs", year, "SELECT_TERM_YEARS");
- }
- if (req.post["Amortize"].empty())
- dict.ShowSection("NotAmortize");
- else
- {
- double P = boost::lexical_cast<double>(
- string_from_currency(req.post["LoanAmt"]));
- double i = req.post.pick("YearlyIntRate", 1.0) / 1200;
- double n = req.post.pick("TermYrs", 1.0) * 12;
- double monthly_payments = (P*i) / (1 - std::pow((1+i), -n));
-
- ctemplate::TemplateDictionary* sub_dict
- = dict.AddSectionDictionary("RegPmtSummary");
- sub_dict->ShowSection("RegPmtSummary");
- sub_dict->SetFormattedValue("MonthlyPmt", "%.2f", monthly_payments);
- dict.ShowSection("Amortize");
- dict.SetValue("SCRIPT_NAME", req.script_name());
- double balance = P;
- int row_num = 0;
- double interest;
- double principal_paid;
- double total_interest = 0;
- do{
- ctemplate::TemplateDictionary* sub_dict2
- = dict.AddSectionDictionary("PaymentEntry");
- sub_dict2->ShowSection("PaymentEntry");
- sub_dict2->SetFormattedValue("Payment", "%.2f", monthly_payments);
- sub_dict2->SetIntValue("ROW_NUM", ++row_num);
- sub_dict2->SetIntValue("ROW_TYPE", (row_num % 2) + 1);
- interest = balance * i;
- total_interest += interest;
- sub_dict2->SetFormattedValue("InterestPaid", "%.2f", interest);
- principal_paid = monthly_payments - interest;
- sub_dict2->SetFormattedValue("PrincipalPaid", "%.2f", principal_paid);
- balance -= principal_paid; // Note: balance can increase.
- sub_dict2->SetFormattedValue("Balance", "%.2f", balance);
- }while(balance > 0);
- sub_dict->SetFormattedValue(
- "RegPmt_TotalIntPd", "%.2f", total_interest);
- sub_dict->SetFormattedValue(
- "RegPmt_TotalPmts", "%.2f", total_interest + P);
- }
- }
- template<typename Request>
- int write_amortization_template(Request& req, response& resp)
- {
- ctemplate::TemplateDictionary dict("amortization");
- fill_amortization_dictionary(dict, req);
- ctemplate::Template* tmpl
- = ctemplate::Template::GetTemplate("amortization.html", ctemplate::STRIP_WHITESPACE);
- std::string h("Content-type: text/html\r\n\r\n");
- write(req.client(), buffer(h));
- int arg = req.get.pick("arg", 2); // 2 is the default.
- // Different, but equivalent ways of writing the output.
- std::string output;
- switch (arg)
- {
- case 1:
- tmpl->Expand(&output, &dict);
- resp<< output;
- break;
- case 2:
- tmpl->Expand(&output, &dict);
- write(req.client(), buffer(output));
- break;
- case 3:
- // // This requires a modified version of Google.cTemplate, so it won't work.
- // std::string s;
- // std::vector<boost::asio::const_buffer> out;
- //
- // tmpl->Expand(&s, &out, &dict);
- // write(req.client(), out);
- break;
- default:
- resp<< "Error!";
- return 1;
- }
- return 0;
- }
- int handle_request(acceptor& a)
- {
- boost::system::error_code ec;
- request req(a.protocol_service());
-
- int ret = 0;
- int num = 0;
- while(!ec && ret == 0)
- {
- response resp;
- ++num;
- // Accepting on a closed request is fine (and more efficient than
- // constantly creating/destructing request objects).
- a.accept(req);
- req.load(parse_all);
- resp<< content_type("text/html")
- << "map size := " << req.post.size() << "<p>";
-
- ret = write_amortization_template(req, resp);
- ret = commit(req, resp, 0, ec);
- }
- return ret;
- }
- int main()
- {
- try{
- service s;
- acceptor a(s);
- for(;;)
- {
- // Keep handling requests until something goes wrong.
- // An exception will be thrown.
- if (handle_request(a))
- break;
- }
-
- return 0;
- }catch(boost::system::system_error& err){
- std::cout<< "Content-type: text/plain\r\n\r\n"
- << "Error (" << err.code() << "): " << err.what();
- return 0;
- }catch(std::exception& e){
- std::cout<< "Content-type: text/html\r\n\r\n"
- << "Exception caught: " << e.what();
- return 0;
- }catch(...){
- std::cout<< "Content-type: text/html\r\n\r\n"
- << "Unknown error!";
- }
- }
- //]