/libs/cgi/example/fcgi/amortization/main.cpp

http://github.com/darrengarvey/cgi · C++ · 204 lines · 137 code · 32 blank · 35 comment · 7 complexity · 1a86786a3337472eb3da461b829b2b2a MD5 · raw file

  1. // -- main.hpp --
  2. //
  3. // Copyright (c) Darren Garvey 2007.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. ////////////////////////////////////////////////////////////////
  9. //
  10. //[fcgi_amort
  11. //
  12. // Amortization Calculator
  13. // -----------------------
  14. //
  15. // This file uses Google cTemplate to show the benefits of using an
  16. // HTML template engine. The code isn't commented yet, but should be
  17. // *reasonably* self-explanatory.
  18. //
  19. // It is a very basic amortization calculator.
  20. //
  21. #include <iostream>
  22. #include <iomanip>
  23. #include <boost/cgi/fcgi.hpp>
  24. #include <boost/algorithm/string/regex.hpp>
  25. #include <ctemplate/template.h>
  26. using namespace boost::fcgi;
  27. /// Convert a string like '$250,000' into one like '250000'.
  28. std::string string_from_currency(std::string amt)
  29. {
  30. // this is much too hardcore, but it works fine...
  31. boost::algorithm::erase_all_regex(amt, boost::regex("[$, ]"));
  32. return amt;
  33. }
  34. /// This function fills a dictionary.
  35. template<typename Request>
  36. void fill_amortization_dictionary(
  37. ctemplate::TemplateDictionary& dict, Request& req)
  38. {
  39. dict.SetValue("LoanAmt", req.post.count("LoanAmt")
  40. ? "$250,000" : req.post["LoanAmt"]);
  41. dict.SetIntValue("YearlyIntRate", req.post.pick("YearlyIntRate", 6.000));
  42. boost::array<std::string, 8> year_opts
  43. = {{ "5", "7", "10", "20", "30", "40", "50" }};
  44. BOOST_FOREACH(std::string& year, year_opts)
  45. {
  46. dict.SetValueAndShowSection("TermYrs", year, "SELECT_TERM_YEARS");
  47. }
  48. if (req.post["Amortize"].empty())
  49. dict.ShowSection("NotAmortize");
  50. else
  51. {
  52. double P = boost::lexical_cast<double>(
  53. string_from_currency(req.post["LoanAmt"]));
  54. double i = req.post.pick("YearlyIntRate", 1.0) / 1200;
  55. double n = req.post.pick("TermYrs", 1.0) * 12;
  56. double monthly_payments = (P*i) / (1 - std::pow((1+i), -n));
  57. ctemplate::TemplateDictionary* sub_dict
  58. = dict.AddSectionDictionary("RegPmtSummary");
  59. sub_dict->ShowSection("RegPmtSummary");
  60. sub_dict->SetFormattedValue("MonthlyPmt", "%.2f", monthly_payments);
  61. dict.ShowSection("Amortize");
  62. dict.SetValue("SCRIPT_NAME", req.script_name());
  63. double balance = P;
  64. int row_num = 0;
  65. double interest;
  66. double principal_paid;
  67. double total_interest = 0;
  68. do{
  69. ctemplate::TemplateDictionary* sub_dict2
  70. = dict.AddSectionDictionary("PaymentEntry");
  71. sub_dict2->ShowSection("PaymentEntry");
  72. sub_dict2->SetFormattedValue("Payment", "%.2f", monthly_payments);
  73. sub_dict2->SetIntValue("ROW_NUM", ++row_num);
  74. sub_dict2->SetIntValue("ROW_TYPE", (row_num % 2) + 1);
  75. interest = balance * i;
  76. total_interest += interest;
  77. sub_dict2->SetFormattedValue("InterestPaid", "%.2f", interest);
  78. principal_paid = monthly_payments - interest;
  79. sub_dict2->SetFormattedValue("PrincipalPaid", "%.2f", principal_paid);
  80. balance -= principal_paid; // Note: balance can increase.
  81. sub_dict2->SetFormattedValue("Balance", "%.2f", balance);
  82. }while(balance > 0);
  83. sub_dict->SetFormattedValue(
  84. "RegPmt_TotalIntPd", "%.2f", total_interest);
  85. sub_dict->SetFormattedValue(
  86. "RegPmt_TotalPmts", "%.2f", total_interest + P);
  87. }
  88. }
  89. template<typename Request>
  90. int write_amortization_template(Request& req, response& resp)
  91. {
  92. ctemplate::TemplateDictionary dict("amortization");
  93. fill_amortization_dictionary(dict, req);
  94. ctemplate::Template* tmpl
  95. = ctemplate::Template::GetTemplate("amortization.html", ctemplate::STRIP_WHITESPACE);
  96. std::string h("Content-type: text/html\r\n\r\n");
  97. write(req.client(), buffer(h));
  98. int arg = req.get.pick("arg", 2); // 2 is the default.
  99. // Different, but equivalent ways of writing the output.
  100. std::string output;
  101. switch (arg)
  102. {
  103. case 1:
  104. tmpl->Expand(&output, &dict);
  105. resp<< output;
  106. break;
  107. case 2:
  108. tmpl->Expand(&output, &dict);
  109. write(req.client(), buffer(output));
  110. break;
  111. case 3:
  112. // // This requires a modified version of Google.cTemplate, so it won't work.
  113. // std::string s;
  114. // std::vector<boost::asio::const_buffer> out;
  115. //
  116. // tmpl->Expand(&s, &out, &dict);
  117. // write(req.client(), out);
  118. break;
  119. default:
  120. resp<< "Error!";
  121. return 1;
  122. }
  123. return 0;
  124. }
  125. int handle_request(acceptor& a)
  126. {
  127. boost::system::error_code ec;
  128. request req(a.protocol_service());
  129. int ret = 0;
  130. int num = 0;
  131. while(!ec && ret == 0)
  132. {
  133. response resp;
  134. ++num;
  135. // Accepting on a closed request is fine (and more efficient than
  136. // constantly creating/destructing request objects).
  137. a.accept(req);
  138. req.load(parse_all);
  139. resp<< content_type("text/html")
  140. << "map size := " << req.post.size() << "<p>";
  141. ret = write_amortization_template(req, resp);
  142. ret = commit(req, resp, 0, ec);
  143. }
  144. return ret;
  145. }
  146. int main()
  147. {
  148. try{
  149. service s;
  150. acceptor a(s);
  151. for(;;)
  152. {
  153. // Keep handling requests until something goes wrong.
  154. // An exception will be thrown.
  155. if (handle_request(a))
  156. break;
  157. }
  158. return 0;
  159. }catch(boost::system::system_error& err){
  160. std::cout<< "Content-type: text/plain\r\n\r\n"
  161. << "Error (" << err.code() << "): " << err.what();
  162. return 0;
  163. }catch(std::exception& e){
  164. std::cout<< "Content-type: text/html\r\n\r\n"
  165. << "Exception caught: " << e.what();
  166. return 0;
  167. }catch(...){
  168. std::cout<< "Content-type: text/html\r\n\r\n"
  169. << "Unknown error!";
  170. }
  171. }
  172. //]