02f62b238b6fc4d2a61a3307755df6e3d878e5de
[chaz/homebank] / src / hb-import-csv.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2018 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "homebank.h"
21
22
23 #include "hb-import.h"
24
25
26 /****************************************************************************/
27 /* Debug macros */
28 /****************************************************************************/
29 #define MYDEBUG 0
30
31 #if MYDEBUG
32 #define DB(x) (x);
33 #else
34 #define DB(x);
35 #endif
36
37 /* our global datas */
38 extern struct HomeBank *GLOBALS;
39 extern struct Preferences *PREFS;
40
41
42 static gchar *hb_csv_strndup (gchar *str, gsize n)
43 {
44 gchar *new_str;
45 gchar *twoquote;
46
47 if (str)
48 {
49 new_str = g_new (gchar, n + 1);
50 if(*str=='\"')
51 {
52 str++; n--;
53 }
54 if(str[n-1]=='\"')
55 n--;
56
57 strncpy (new_str, str, n);
58 new_str[n] = '\0';
59
60 // replace ""
61 twoquote = strstr(new_str, "\"\"");
62 if(twoquote)
63 strcpy (twoquote, twoquote+1);
64
65 //todo: replace &amp; &lt; &gt; &apos; &quot; ??
66
67 }
68 else
69 new_str = NULL;
70
71 return new_str;
72 }
73
74
75 static gchar *hb_csv_find_delimiter(gchar *string)
76 {
77 gchar *s = string;
78 gboolean enclosed = FALSE;
79
80 while( *s != '\0' )
81 {
82 if( *s == ';' && enclosed == FALSE )
83 break;
84
85 if( *s == '\"' )
86 {
87 enclosed = !enclosed;
88 }
89
90 s++;
91 }
92
93 return s;
94 }
95
96
97 gboolean hb_csv_row_valid(gchar **str_array, guint nbcolumns, gint *csvtype)
98 {
99 gboolean valid = TRUE;
100 guint i;
101 extern int errno;
102
103 #if MYDEBUG == 1
104 gchar *type[5] = { "string", "date", "int", "double" };
105 gint lasttype;
106 #endif
107
108 DB( g_print("\n** hb_string_csv_valid: init %d\n", valid) );
109
110 DB( g_print(" -> length %d, nbcolumns %d\n", g_strv_length( str_array ), nbcolumns) );
111
112 if( g_strv_length( str_array ) != nbcolumns )
113 {
114 valid = FALSE;
115 goto csvend;
116 }
117
118 for(i=0;i<nbcolumns;i++)
119 {
120 #if MYDEBUG == 1
121 lasttype = csvtype[i];
122 #endif
123
124 if(valid == FALSE)
125 {
126 DB( g_print(" -> fail on column %d, type: %s\n", i, type[lasttype]) );
127 break;
128 }
129
130 DB( g_print(" -> control column %d, type: %d, valid: %d '%s'\n", i, lasttype, valid, str_array[i]) );
131
132 switch( csvtype[i] )
133 {
134 case CSV_DATE:
135 valid = hb_string_isdate(str_array[i]);
136 break;
137 case CSV_STRING:
138 valid = hb_string_isprint(str_array[i]);
139 break;
140 case CSV_INT:
141 valid = hb_string_isdigit(str_array[i]);
142 break;
143 case CSV_DOUBLE :
144
145 //todo: use strtod (to take care or . or ,)
146 g_ascii_strtod(str_array[i], NULL);
147 //todo : see this errno
148 if( errno )
149 {
150 DB( g_print("errno: %d\n", errno) );
151 valid = FALSE;
152 }
153 break;
154 }
155 }
156
157 csvend:
158
159 DB( g_print(" --> return %d\n", valid) );
160
161 return valid;
162 }
163
164
165 gchar **hb_csv_row_get(gchar *string, gchar *delimiter, gint max_tokens)
166 {
167 GSList *string_list = NULL, *slist;
168 gchar **str_array, *s;
169 guint n = 0;
170 gchar *remainder;
171
172 g_return_val_if_fail (string != NULL, NULL);
173 g_return_val_if_fail (delimiter != NULL, NULL);
174 g_return_val_if_fail (delimiter[0] != '\0', NULL);
175
176 if (max_tokens < 1)
177 max_tokens = G_MAXINT;
178
179 remainder = string;
180 s = hb_csv_find_delimiter (remainder);
181 if (s)
182 {
183 gsize delimiter_len = strlen (delimiter);
184
185 while (--max_tokens && s && *s != '\0')
186 {
187 gsize len;
188
189 len = s - remainder;
190 string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
191 DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) );
192
193 n++;
194 remainder = s + delimiter_len;
195 s = hb_csv_find_delimiter (remainder);
196 }
197 }
198 if (*string)
199 {
200 gsize len;
201
202 len = s - remainder;
203 n++;
204 string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
205 DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) );
206 }
207
208 str_array = g_new (gchar*, n + 1);
209
210 str_array[n--] = NULL;
211 for (slist = string_list; slist; slist = slist->next)
212 str_array[n--] = slist->data;
213
214 g_slist_free (string_list);
215
216 return str_array;
217 }
218
219
220 GList *homebank_csv_import(gchar *filename, ImportContext *ictx)
221 {
222 GIOChannel *io;
223 GList *list = NULL;
224 static gint csvtype[7] = {
225 CSV_DATE,
226 CSV_INT,
227 CSV_STRING,
228 CSV_STRING,
229 CSV_STRING,
230 CSV_DOUBLE,
231 CSV_STRING,
232 };
233
234 DB( g_print("\n[import] homebank csv\n") );
235
236 io = g_io_channel_new_file(filename, "r", NULL);
237 if(io != NULL)
238 {
239 gchar *tmpstr;
240 gsize length;
241 gint io_stat;
242 gboolean isvalid;
243 gint count = 0;
244 gint error = 0;
245 Account *tmp_acc;
246 Payee *payitem;
247 Category *catitem;
248 GError *err = NULL;
249
250
251 gchar *accname = g_strdup_printf(_("(account %d)"), da_acc_get_max_key() + 1);
252 tmp_acc = import_create_account(accname, NULL);
253 g_free(accname);
254
255
256 if( ictx->encoding != NULL )
257 {
258 g_io_channel_set_encoding(io, ictx->encoding, NULL);
259 }
260
261 for(;;)
262 {
263 io_stat = g_io_channel_read_line(io, &tmpstr, &length, NULL, &err);
264 if( io_stat == G_IO_STATUS_EOF)
265 break;
266 if( io_stat == G_IO_STATUS_ERROR )
267 {
268 DB (g_print(" + ERROR %s\n",err->message));
269 break;
270 }
271 if( io_stat == G_IO_STATUS_NORMAL)
272 {
273 if( *tmpstr != '\0' )
274 {
275 gchar **str_array;
276
277 count++;
278
279 hb_string_strip_crlf(tmpstr);
280 DB( g_print("\n (row-%04d) ->|%s|<-\n", count, tmpstr) );
281
282 // 0:date; 1:paymode; 2:info; 3:payee, 4:wording; 5:amount; 6:category; 7:tags
283 str_array = hb_csv_row_get(tmpstr, ";", 8);
284 isvalid = hb_csv_row_valid(str_array, 8, csvtype);
285
286 DB( g_print(" valid %d, '%s'\n", isvalid, tmpstr) );
287
288 if( !isvalid )
289 {
290 g_warning ("csv parse: line %d, invalid column count or data", count);
291 error++;
292 //todo log line in error to report user
293 }
294 else
295 {
296 Transaction *newope = da_transaction_malloc();
297
298 //DB( g_print(" ->%s\n", tmpstr ) );
299
300 newope->date = hb_date_get_julian(str_array[0], ictx->datefmt);
301 if( newope->date == 0 )
302 {
303 g_warning ("csv parse: line %d, parse date failed", count);
304 ictx->cnt_err_date++;
305 }
306
307 newope->paymode = atoi(str_array[1]);
308 newope->info = g_strdup(str_array[2]);
309
310 /* payee */
311 g_strstrip(str_array[3]);
312 payitem = da_pay_get_by_name(str_array[3]);
313 if(payitem == NULL)
314 {
315 payitem = da_pay_malloc();
316 payitem->name = g_strdup(str_array[3]);
317 payitem->imported = TRUE;
318 da_pay_append(payitem);
319
320 if( payitem->imported == TRUE )
321 ictx->cnt_new_pay += 1;
322 }
323
324 newope->kpay = payitem->key;
325 newope->memo = g_strdup(str_array[4]);
326 newope->amount = hb_qif_parser_get_amount(str_array[5]);
327
328 /* category */
329 g_strstrip(str_array[6]);
330 catitem = da_cat_append_ifnew_by_fullname(str_array[6], TRUE);
331 if( catitem != NULL )
332 {
333 newope->kcat = catitem->key;
334
335 if( catitem->imported == TRUE && catitem->key > 0 )
336 ictx->cnt_new_cat += 1;
337 }
338
339 /* tags */
340 transaction_tags_parse(newope, str_array[7]);
341
342
343 newope->kacc = tmp_acc->key;
344 //newope->kxferacc = accnum;
345
346 newope->flags |= OF_ADDED;
347
348 if( newope->amount > 0)
349 newope->flags |= OF_INCOME;
350
351 /*
352 DB( g_print(" storing %s : %s : %s :%s : %s : %s : %s : %s\n",
353 str_array[0], str_array[1], str_array[2],
354 str_array[3], str_array[4], str_array[5],
355 str_array[6], str_array[7]
356 ) );
357 */
358
359 list = g_list_append(list, newope);
360
361 g_strfreev (str_array);
362 }
363 }
364 g_free(tmpstr);
365 }
366
367 }
368 g_io_channel_unref (io);
369
370 /*
371 ui_dialog_msg_infoerror(data->window, error > 0 ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO,
372 _("Transaction CSV import result"),
373 _("%d transactions inserted\n%d errors in the file"),
374 count, error);
375 */
376 }
377
378
379 return list;
380 }
381
382
This page took 0.060032 seconds and 4 git commands to generate.