2 * Read bank transactions and generate account bookings.
5 * Lines starting with '+' or '-': Match for name and description. Two regular
6 * expressions, separated by '~'. The '+' matches CREDIT transactions (adds to
7 * the bank account); the '-' matches DEBIT transactions.
9 * Other lines are content of the template. Parts enclosed in '~' characters are substituted by fields
10 * from the input. Possibilities are: "$D" -> booking date, "$N" -> name, "$A" -> amount,
11 * anything else is taken as a regular expression that is extracted from the description.
17 #include <AXE/String.h>
24 regex description_match;
30 BankTemplate() : name_match(""), description_match(".*")
36 BankTemplate(bool cr, String nm, String dm)
37 : name_match(nm), description_match(dm)
43 BankTemplate(const BankTemplate & b)
44 : name_match(b.name_match), description_match(b.description_match)
47 text_template = b.text_template;
50 void add_to_template(String s)
52 text_template += s + "\n";
55 bool matches(String deb_cred, String name, String descr);
57 String substitute_template(String date, String name, String amount, String descr);
60 bool BankTemplate::matches(String deb_cred, String name, String descr)
62 return (deb_cred == "C") == Credit && name == name_match && descr == description_match;
65 String BankTemplate::substitute_template(String date, String name, String amount, String descr)
67 String text(text_template);
69 int sub_start, sub_len;
71 sub_start = text.index('~');
73 while (sub_start != -1)
76 while (text[sub_start + sub_len] != '~')
81 String match = text(sub_start, sub_len);
82 if (~match == 4 && match[1] == '$')
87 text(sub_start, sub_len) = amount;
91 text(sub_start, sub_len) = name;
95 text(sub_start, sub_len) = date(6,2) + String("-") + date(4,2) + String("-") + date(0,4);
99 text(sub_start, sub_len) = String("INVALID SUBSTITUTION");
107 String extraction = descr(regex(match));
108 text(sub_start, sub_len) = extraction;
111 sub_start = text.index('~');
117 std::vector<BankTemplate> read_templates(const char * filename);
118 void read_mutations(const char *filename, std::vector<BankTemplate> templates);
119 std::vector<String> parse_csv(String line, char delim);
121 // Fields in the bank record.
123 const int DEBIT_CREDIT = 3;
124 const int AMOUNT = 4;
126 const int BOOKING_DATE = 7;
127 const int DESCRIP1 = 10;
128 const int DESCRIP2 = 11;
129 const int DESCRIP3 = 12;
130 const int DESCRIP4 = 13;
131 const int DESCRIP5 = 14;
132 const int DESCRIP6 = 15;
136 //read_templates("Bank.templ");
137 std::vector<BankTemplate> templates = read_templates("Bank.templ");
139 read_mutations("mut.txt", templates);
142 std::vector<BankTemplate> read_templates(const char * filename)
144 std::ifstream in(filename);
148 std::vector<BankTemplate> templates;
154 if (~line > 0 && (line[0] == '-' || line[0] == '+'))
156 // Start a new template
160 templates.push_back(bt);
165 int separator = line.index('~');
166 bt = BankTemplate(line[0] == '+', line(1, separator -1), line(separator + 1, ~line - separator -1));
170 // Add to the template text
171 bt.add_to_template(line);
175 templates.push_back(bt);
180 void read_mutations(const char *filename, std::vector<BankTemplate> templates)
184 std::ifstream in(filename);
187 std::vector<String> bank_record;
193 //std::cout << line << "\n";
195 bank_record = parse_csv(line, ',');
197 //std::cout << " " << bank_record[BOOKING_DATE] << " " <<bank_record[DEBIT_CREDIT] << " " <<bank_record[NAME] << ": " << bank_record[AMOUNT];
198 description = bank_record[DESCRIP1];
199 for (i = DESCRIP2; i <= DESCRIP6; i++)
201 description += " " + bank_record[i];
203 //std::cout << " " << description << "\n";
205 // Find a template for this bank record
208 while (!templates[i].matches(bank_record[DEBIT_CREDIT], bank_record[NAME], description) && i < templates.size())
212 if (i != templates.size())
214 std::cout << templates[i].substitute_template(bank_record[BOOKING_DATE], bank_record[NAME],
215 bank_record[AMOUNT], description);
219 std::cerr << "Bank record does not match any tempplate:\n";
224 std::vector<String> parse_csv(String line, char delim)
226 std::vector<String> record;
228 bool inquotes = false;
229 int start = 0, len = 0;
231 for (i = 0; i < ~line; i++)
233 if (!inquotes && line[i] == '"')
237 else if (inquotes && line[i] == '"')
241 else if (!inquotes && line[i] == delim)
243 // Delimiter: copy the substring to the record
245 String field = line(start, len);
246 record.push_back(field);