sycsvpro 0.1.4 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sycsvpro (0.1.4)
4
+ sycsvpro (0.1.7)
5
5
  gli (= 2.9.0)
6
6
  timeleap (~> 0.0.1)
7
7
 
data/README.md CHANGED
@@ -16,21 +16,32 @@ Processing of csv files. *sycsvpro* offers following functions
16
16
  * create or edit a Ruby script
17
17
  * list scripts available optionally with methods (since version 0.0.7)
18
18
  * execute a Ruby script file that operates a csv file
19
- * create a table from a source file with dynamically create columns (since version 0.1.4)
19
+ * create a table from a source file with dynamically create columns (since
20
+ version 0.1.4)
21
+ * join two file based on a joint column value (since version 0.1.7)
20
22
 
21
23
  To get help type
22
24
 
23
25
  $ sycsvpro -h
24
26
 
25
- In the following examples we assume the following file
27
+ In the following examples we assume the following files 'machines.csv' and
28
+ 'region.csv'
26
29
 
27
30
  ```
28
- customer;machine;control;drive;motor;date;contract
29
- hello;h1;con123;dri120;mot100;1.01.3013;1
30
- hello;h2;con123;dri130;mot110;1.02.3012;1
31
- indix;i1;con456;dri130;mot090;5.11.3013;1
32
- chiro;c1;con333;dri110;mot100;1.10.3011;1
33
- chiro;c2;con331;dri100;mot130;3.05.3010;1
31
+ customer;machine;control;drive;motor;date;contract;price;c-id
32
+ hello;h1;con123;dri120;mot100;1.01.3013;1;2.5;123
33
+ hello;h2;con123;dri130;mot110;1.02.3012;1;12.1;123
34
+ indix;i1;con456;dri130;mot090;5.11.3013;1;23.24;345
35
+ chiro;c1;con333;dri110;mot100;1.10.3011;1;122.15;456
36
+ chiro;c2;con331;dri100;mot130;3.05.3010;1;25.3;456
37
+ ```
38
+
39
+ ```
40
+ region;country;c-id
41
+ R1;DE,123
42
+ R2;AT;234
43
+ R3;US;345
44
+ R4;CA;456
34
45
  ```
35
46
 
36
47
  Analyze
@@ -114,7 +125,8 @@ Count all customers (key column) in rows 2 to 20 that have machines that start
114
125
  with *h* and have a contract valid beginning after 1.1.2000. Add a sum row with
115
126
  title Total at column 1
116
127
 
117
- $ sycsvpro -f in.csv -o out.csv count -r 2-20 -k 0:customer -c 1:/^h/,5:">1.1.2000" --df "%d.%m.%Y" -s "Total:1"
128
+ $ sycsvpro -f in.csv -o out.csv count -r 2-20 -k 0:customer
129
+ -c 1:/^h/,5:">1.1.2000" --df "%d.%m.%Y" -s "Total:1"
118
130
 
119
131
  The result in file out.csv is
120
132
 
@@ -143,12 +155,30 @@ The aggregation result in out.csv is
143
155
  indix;1
144
156
  chiro;2
145
157
 
158
+ Table
159
+ -----
160
+ Analyze the contract revenue per customer and per year
161
+
162
+ $ sycsvpro -f in.csv -o out.csv table
163
+ -h "Customer,c5=~/\\.(\\d{4})/"
164
+ -k c1
165
+ -c "c5=~/\\.\\d{4})/:+n1"
166
+
167
+ The table result will be in out.csv
168
+
169
+ $ cat out.csv
170
+ Customer;3013;3012;3011;3010
171
+ hello;2.5;12.1;0;0
172
+ indix;23.24;0;0;0
173
+ chiro;0;0;122.15;25.3
174
+
146
175
  Calc
147
176
  ----
148
177
  Process arithmetic operations on the contract count and create a target column
149
178
  and a sum which is added at the end of the result file
150
179
 
151
- $ sycsvpro -f in.csv -o out.csv calc -r 2-20 -h *,target -c 6:*2,7:target=c6*10
180
+ $ sycsvpro -f in.csv -o out.csv calc -r 2-20 -h *,target
181
+ -c 6:*2,7:target=c6*10
152
182
 
153
183
  $ cat out.csv
154
184
  customer;machine;control;drive;motor;date;contract;target
@@ -162,6 +192,26 @@ and a sum which is added at the end of the result file
162
192
  In the sum row non-numbers in the colums are converted to 0. Therefore column 0
163
193
  is summed up to 0 as all strings are converted to 0.
164
194
 
195
+ Join
196
+ ----
197
+ Join the machine and contract file with columns from the customer address file
198
+
199
+ $ sycsvpro -f in.csv -o out.csv join address.csv -c 0,1
200
+ -p 2,1
201
+ -i "COUNTRY,REGION"
202
+ -j "3=8"
203
+
204
+ This will create the result
205
+
206
+ ```
207
+ customer;COUNTRY;REGION;machine;control;drive;motor;date;contract;price;c-id
208
+ hello;DE;R1;h1;con123;dri120;mot100;1.01.3013;1;2.5;123
209
+ hello;DE;R1;h2;con123;dri130;mot110;1.02.3012;1;12.1;123
210
+ indix;US;R3i1;con456;dri130;mot090;5.11.3013;1;23.24;345
211
+ chiro;CA;R4;c1;con333;dri110;mot100;1.10.3011;1;122.15;456
212
+ chiro;CA;R4;c2;con331;dri100;mot130;3.05.3010;1;25.3;456
213
+ ```
214
+
165
215
  Sort
166
216
  ----
167
217
  Sort rows on specified columns as an example sort rows based on customer
@@ -198,7 +248,7 @@ the name script.rb and a method call_me
198
248
  List
199
249
  ----
200
250
  List the scripts, insert-file or all scripts available in the scripts directory
201
- which is also displayed
251
+ which is also displayed. Comments before methods are also displayed
202
252
 
203
253
  script directory: ~/.syc/sycsvpro/scripts
204
254
  $ sycsvpro list -m
@@ -257,6 +307,9 @@ end with _column_ or _columns_ dependent if a value or an array should be
257
307
  returned. You can find the *rows* and *write_to* methods at
258
308
  _lib/sycsvpro/dsl.rb_.
259
309
 
310
+ Examples for scripts using sycsvpro can be found at
311
+ [sugaryourcoffee/sycsvpro-scripts](https://github.com/sugaryourcoffee/sycsvpro-scripts)
312
+
260
313
  Working with sycsvpro
261
314
  =====================
262
315
 
@@ -316,19 +369,58 @@ Version 0.1.4
316
369
  * Associate values to multi keys
317
370
  * Create values based on arithmetic operations of source table data
318
371
  Example
319
- `sycsvpro -f in.csv -o out.csv table -h "c4,c5,c0=~/\\.(\\d{4})/" \
320
- -k "c4,c5" \
321
- -c "c0=~/\\.(\\d{4})/:+n1"`
322
- h:: the header is created from the source table header of column 4 and 5.
323
- Another header column is created dynamicall based on the year part of a
324
- date in column 0
325
- k:: the key is based on source table of column 4 and 5
326
- c:: the column operation is in the form HeaderName:Operation. In this case the
327
- HeaderName is dynamically determined based on column 0 and added the value
328
- of column 1 to this column that is associated to the key
372
+ `sycsvpro -f in.csv -o out.csv table -h "c4,c5,c0=~/\\.(\\d{4})/"
373
+ -k "c4,c5"
374
+ -c "c0=~/\\.(\\d{4})/:+n1"`
375
+ + h the header is created from the source table header of column 4 and 5.
376
+ Another header column is created dynamicall based on the year part of
377
+ a date in column 0
378
+ + k the key is based on source table of column 4 and 5
379
+ + c the column operation is in the form HeaderName:Operation. In this case
380
+ the HeaderName is dynamically determined based on column 0 and added
381
+ the value of column 1 to this column that is associated to the key
329
382
 
330
383
  c4, n4, d4 are string, number and date values respectively
331
384
 
385
+ Version 0.1.5
386
+ -------------
387
+ * Add a sum row after the heading or at the end of file like so
388
+ `sycsvpro -f in.csv -o out.csv table -h "c4,c5,c0=~/\\.(\\d{4})/"
389
+ -k "c4,c5"
390
+ -c "c0=~/\\.(\\d{4})/:+n1"
391
+ -s "c0=~/\\.(\\d{4})/"`
392
+ This will sum up the dynamically created column.
393
+
394
+ Version 0.1.6
395
+ -------------
396
+ * Commas within columns expression are now ignored while splitting columns of
397
+ table columns
398
+ * Table takes a number format now with `--nf DE` which will convert numbers
399
+ from DE locale like 1.000,00 to 1000.00
400
+ * Table uses a precision for numbers. Default is 2. Can be assigned with `pr: 2`
401
+
402
+ Version 0.1.7
403
+ -------------
404
+ * Calc can now be used not to only do arithmetic operations on columns but also
405
+ string operations. Ultimately any valid Ruby command can be used to process a
406
+ column value
407
+ `sycsvpro -f customer.csv -o customer-number.csv calc
408
+ -h "Customer_ID,Customer,Country"
409
+ -r "1-eof"
410
+ -c "2:s0.scan(/^([A-Z]+)\\//).flatten[0],
411
+ 0:s0.scan(/(?<=\\/)(.*)$/).flatten[0],1:s1"
412
+ * Join is a new class that joins to tables based on a joint column value
413
+ `sycsvpro -f infile.csv -o outfile.csv join source.csv -c "2,4"
414
+ -j "1=3"
415
+ -p "1,3"
416
+ -h "*"
417
+ -i "A,B"`
418
+ This will join infile.csv with source.csv based on the join columns (j "1=3").
419
+ From source.csv columns 2 and 4 (-c "2,4") will be inserted at column
420
+ positions 1 and 3 (-p "1,3"). The header will be used from the infile.csv
421
+ (-h "*") supplemented by the columns A and B (-i "A,B") that will also be
422
+ positioned at column 1 and 3 (-p "1,3").
423
+
332
424
  Installation
333
425
  ============
334
426
  [![Gem Version](https://badge.fury.io/rb/sycsvpro.png)](http://badge.fury.io/rb/sycsvpro)
data/bin/sycsvpro CHANGED
@@ -89,12 +89,13 @@ command :extract do |c|
89
89
  end
90
90
  end
91
91
 
92
- desc 'Collect values of specified rows and columns from the file and group them in categories'
92
+ desc 'Collect values of specified rows and columns from the file and group '+
93
+ 'them in categories'
93
94
  command :collect do |c|
94
95
 
95
96
  c.desc 'Rows to consider for collection'
96
97
  c.arg_name 'ROW1,ROW2,ROW10-ROW30,45-EOF,REGEXP'
97
- c.flag [:r, :row], :must_match => row_regex #/\d+(?:,\d+|-\d+|-eof|,\/.*\/)*|\/.*\/(?:,\/.*\/|\d+)*/i
98
+ c.flag [:r, :row], :must_match => row_regex
98
99
 
99
100
  c.desc 'Columns to collect values from'
100
101
  c.arg_name 'CATEGORY1:COL1,COL2,COL10-COL30+CATEGORY2:COL3-COL9'
@@ -120,7 +121,7 @@ desc 'Allocate specified columns from the file to a key value'
120
121
  command :allocate do |c|
121
122
  c.desc 'Rows to consider'
122
123
  c.arg_name '1,2,10-30,45-EOF,REGEXP'
123
- c.flag [:r, :row], :must_match => row_regex #/\d+(?:,\d+|-\d+|-eof|,\/.*\/)*|\/.*\/(?:,\/.*\/|\d+)*/i
124
+ c.flag [:r, :row], :must_match => row_regex
124
125
 
125
126
  c.desc 'Key to allocate columns to'
126
127
  c.arg_name '0'
@@ -147,7 +148,8 @@ command :allocate do |c|
147
148
  end
148
149
  end
149
150
 
150
- desc 'Creates a script/insert file or opens a script/insert file for editing if it exists'
151
+ desc 'Creates a script/insert file or opens a script/insert file for editing '+
152
+ 'if it exists'
151
153
  command :edit do |c|
152
154
  c.desc 'Name of the script/insert file'
153
155
  c.arg_name 'SCRIPT_NAME.rb|INSERT_NAME.ins'
@@ -159,12 +161,14 @@ command :edit do |c|
159
161
 
160
162
  c.action do |global_options,options,args|
161
163
  script_creator = Sycsvpro::ScriptCreator.new(dir: sycsvpro_directory,
162
- script: options[:s], method: options[:m])
164
+ script: options[:s],
165
+ method: options[:m])
163
166
  system "vi #{script_creator.script_file}"
164
167
  end
165
168
  end
166
169
 
167
- desc 'Lists script or insert files in the scripts directory with optionally listing methods of script files'
170
+ desc 'Lists script or insert files in the scripts directory with optionally '+
171
+ 'listing methods of script files'
168
172
  command :list do |c|
169
173
  c.desc 'Type of script (Ruby, insert or all files)'
170
174
  c.default_value 'script'
@@ -235,7 +239,7 @@ command :count do |c|
235
239
 
236
240
  c.desc 'Rows to consider'
237
241
  c.arg_name '1,2,10-30,45-EOF,REGEXP'
238
- c.flag [:r, :row], :must_match => row_regex #/\d+(?:,\d+|-\d+|-eof|,\/.*\/)*|\/.*\/(?:,\/.*\/|\d+)*/i
242
+ c.flag [:r, :row], :must_match => row_regex
239
243
 
240
244
  c.desc 'Columns to count where columns 2 and 3 are counted conditionally'
241
245
  c.arg_name '1,2:<14.2.2014,10-30,3:>10'
@@ -274,7 +278,7 @@ command :aggregate do |c|
274
278
 
275
279
  c.desc 'Rows to consider'
276
280
  c.arg_name '1,2,10-30,45-EOF,REGEXP'
277
- c.flag [:r, :row], :must_match => row_regex #/\d+(?:,\d+|-\d+|-eof|,\/.*\/)*|\/.*\/(?:,\/.*\/|\d+)*/i
281
+ c.flag [:r, :row], :must_match => row_regex
278
282
 
279
283
  c.desc 'Columns to count'
280
284
  c.arg_name '1,2-4'
@@ -311,18 +315,27 @@ command :table do |c|
311
315
  c.arg_name '1,2,10-30,45-EOF,REGEXP'
312
316
  c.flag [:r, :row], :must_match => row_regex
313
317
 
314
- c.desc 'Header can be defined by Words (Year), references to source header (c1) and dynamically created header values (c1+c2,c0=~/\\.(\\d{4})/)'
318
+ c.desc 'Header can be defined by Words (Year), references to source header '+
319
+ '(c1) and dynamically created header values (c1+c2,c0=~/\\.(\\d{4})/)'
315
320
  c.arg_name "COL_A,c6,c2+c4,c0=~/\\.(\\d{4})/"
316
321
  c.flag [:h, :header]
317
322
 
318
- c.desc 'Key to that the other columns are associated to. A key can be created dynamically'
323
+ c.desc 'Key to that the other columns are associated to. A key can be '+
324
+ 'created dynamically'
319
325
  c.arg_name "c0=~/\\.(\\d{4})/,c6"
320
326
  c.flag [:k, :key]
321
327
 
322
- c.desc 'Columns to be associated to the key. Columns are identified by the column name. The operation to create the column value is separated by a colon (:) from the column name'
328
+ c.desc 'Columns to be associated to the key. Columns are identified by the '+
329
+ 'column name. The operation to create the column value is separated '+
330
+ 'by a colon (:) from the column name'
323
331
  c.arg_name "c0=~/\\.(\\d{4})/:+n1,Value:+n2"
324
332
  c.flag [:c, :col]
325
333
 
334
+ c.desc 'Adds a sum row after the heading or at the end of the file for col '+
335
+ 'values'
336
+ c.arg_name "TOP|EOF:c0=~/\\.(\\d{4})/,Value"
337
+ c.flag [:s, :sum]
338
+
326
339
  c.desc 'Format of date values'
327
340
  c.arg_name '%d.%m.%Y|%m/%d/%Y|...'
328
341
  c.flag [:df]
@@ -341,20 +354,76 @@ command :table do |c|
341
354
  rows: options[:r],
342
355
  header: options[:h],
343
356
  key: options[:k],
344
- cols: options[:c])
357
+ cols: options[:c],
358
+ sum: options[:s])
345
359
  table.execute
346
360
  puts "done"
347
361
  end
348
362
 
349
363
  end
350
364
 
365
+ desc 'Join two files based on a joint column value'
366
+ arg_name 'SOURCE_FILE'
367
+ command :join do |c|
368
+ c.desc 'Rows to consider'
369
+ c.arg_name '1,2,10-30,45-EOF,REGEXP'
370
+ c.flag [:r, :row], :must_match => row_regex
371
+
372
+ c.desc 'Columns to merge into the infile'
373
+ c.arg_name '1,5,7'
374
+ c.flag [:c, :cols], :must_match => /^\d+(?:,\d+)*/
375
+
376
+ c.desc 'The position at which column position to insert the columns within '+
377
+ 'the infile. The sequence of the position is assigned to the columns '+
378
+ 'to be inserted'
379
+ c.arg_name '5,1'
380
+ c.flag [:p, :pos], :must_match => /^\d+(?:,\d+)*/
381
+
382
+ c.desc 'The join columns in the source file, which contains the columns to '+
383
+ 'be inserted into the infile'
384
+ c.arg_name '2=1'
385
+ c.flag [:j, :join], :must_match => /^\d+=\d+$/
386
+
387
+ c.desc 'Indicates whether the infile headerless'
388
+ c.default_value false
389
+ c.switch [:headerless]
390
+
391
+ c.desc 'Header columns of the infile'
392
+ c.arg_name '*,COL1,COL2'
393
+ c.default_value '*'
394
+ c.flag [:h, :header]
395
+
396
+ c.desc 'Header columns to be used for the inserted columns from the source '+
397
+ 'file. The position (-p 5,1) determines where to insert the header '+
398
+ 'columns'
399
+ c.arg_name 'INS_COL1,INS_COL2'
400
+ c.flag [:i, :insert]
401
+
402
+ c.action do |global_options,options,args|
403
+ join = Sycsvpro::Join.new(infile: global_options[:f],
404
+ outfile: global_options[:o],
405
+ source: args[0],
406
+ rows: options[:r],
407
+ cols: options[:c],
408
+ pos: options[:p],
409
+ joins: options[:j],
410
+ headerless: options[:headerless],
411
+ header: options[:h],
412
+ insert_header: options[:i])
413
+ print 'Joining...'
414
+ join.execute
415
+ print 'done'
416
+ end
417
+ end
418
+
351
419
  desc 'Sort rows based on column values'
352
420
  command :sort do |c|
353
421
  c.desc 'Rows to consider'
354
422
  c.arg_name '1,2,10-30,45-EOF,REGEXP'
355
- c.flag [:r, :row], :must_match => row_regex #/\d+(?:,\d+|-\d+|-eof|,\/.*\/)*|\/.*\/(?:,\/.*\/|\d+)*/i
423
+ c.flag [:r, :row], :must_match => row_regex
356
424
 
357
- c.desc 'Columns to sort based on a type (n = number, s = string, d = date) and its value'
425
+ c.desc 'Columns to sort based on a type (n = number, s = string, d = date) '+
426
+ 'and its value'
358
427
  c.arg_name 'n:1,s:2-5,d:7'
359
428
  c.flag [:c, :col], :must_match => /[d|n|s]:\d+(?:-\d+|,[d|n|s]:\d+)*/
360
429
 
@@ -443,29 +512,33 @@ command :map do |c|
443
512
  end
444
513
  end
445
514
 
446
- desc 'Process math operations on columns. Optionally add a sum row'
515
+ desc 'Process operations on columns. Optionally add a sum row for columns with'+
516
+ 'number values'
447
517
  command :calc do |c|
448
518
  c.desc 'The first non-empty column is considered the header. '+
449
- 'If additional columns are created then *,COL1,COL2 will create the additional header '+
450
- 'columns COL1 and COL2'
451
- c.arg_name '*,COL2,COL2'
519
+ 'If additional columns are created then *,COL1,COL2 will create '+
520
+ 'the additional header columns COL1 and COL2. It is also possible '+
521
+ 'to specify different header columns like COL1,COL2,COL3'
522
+ c.arg_name '*,COL2,COL2|COL1,COL2,COL3'
452
523
  default_value '*'
453
- c.flag [:h, :header], :must_match => /\*(?:,\w+)*/
524
+ c.flag [:h, :header], :must_match => /^[*|\w ]+(?:,[\w ]+)*/
454
525
 
455
526
  c.desc 'Rows to consider for calculations'
456
527
  c.arg_name 'ROW1,ROW2-ROW10,45-EOF,REGEXP'
457
- c.flag [:r, :row], :must_match => row_regex #/\d+(?:,\d+|-\d+|-eof|,\/.*\/)*|\/.*\/(?:,\/.*\/|\d+)*/i
528
+ c.flag [:r, :row], :must_match => row_regex
458
529
 
459
- c.desc 'Column to do calculations on'
460
- c.arg_name 'COL1:*2,COL2:-C3,COL3:*2+(4+C5),COL6:NEW_COL=C1+5'
461
- #c.flag [:c, :col], :must_match => /\d+:(?:[\*\/\+\-]|\w+=[\d|(]*)[\*\/\+\-\dc()]*(?:,\d+:(?:[\*\/\+\-]|\w+=[\d|(]*)[\*\/\+\-\dc()]*)*/
462
- c.flag [:c, :col], :must_match => /\d+:(?:[\*\+\-\/\d\w=\[\],\.:()]*)/
530
+ c.desc 'Column to do operations on. s0 = String in column 0, c1 = number '+
531
+ 'in column 1 and d2 = date in column 2. Examples: 2:c1+1,3:s0,'+
532
+ '4:s0.scan(/(\\d+)\//).flatten[0]'
533
+ c.arg_name "COL1:*2,COL2:-C3,COL3:*2+(4+C5)"
534
+ c.flag [:c, :col], :must_match => /^\d+:.+/
463
535
 
464
536
  c.desc 'Date format of date columns'
465
537
  c.arg_name '%d.%m.%Y|%Y-%m-%d|...'
466
538
  c.flag [:df]
467
539
 
468
- c.desc 'Indicate to add a sum row'
540
+ c.desc 'Indicate to add a sum row at end of file. Will sum up values with '+
541
+ 'numbers. Columns with non-number values will be set to 0.'
469
542
  c.switch [:s, :sum]
470
543
 
471
544
  c.action do |global_options,options,args|
@@ -6,9 +6,42 @@ require 'date'
6
6
  # Operating csv files
7
7
  module Sycsvpro
8
8
 
9
- # Processes arithmetic operations on columns of a csv file. A column value has to be a number.
10
- # Possible operations are +, -, * and /. It is also possible to use values of columns as an
11
- # operator like c1*2 will multiply the value of column 1 with 2.
9
+ # Processes operations on columns of a csv file.
10
+ #
11
+ # A column value has to be a number in case of arithmetical operations.
12
+ #
13
+ # Possible operations are +, -, *, /, % and **.
14
+ #
15
+ # It is possible to use values of columns as an operator like multiply
16
+ # column 1 of the csv file with 2 and assign it to column 4 of the result
17
+ # file: c1*2
18
+ #
19
+ # Other values might be dates or strings.
20
+ #
21
+ # d1:: date value in column 1
22
+ # s2:: string value in column 2
23
+ # c3:: number value in column 3
24
+ #
25
+ # To assign a string from column 1 of the csv file to column 3 of the
26
+ # resulting file you can do like so: 3:s1
27
+ #
28
+ # You can also use Ruby expressions to assign values: 0:[d1,d2,d3].min - This
29
+ # will assign the least date value from columns 1, 2 and 3 to column 0.
30
+ #
31
+ # Note: If you assign a value to column 1 and subsequently are using column 1
32
+ # in other assignments then column 1 will have the result of a previous
33
+ # operation.
34
+ #
35
+ # Example:
36
+ # Having a row "CA/123456" and you want to have 123456 in column 0
37
+ # of the resulting csv file and CA in column 2. If you conduct following
38
+ # operations it will fail
39
+ # 1:s0.scan(/\/(.+)/).flatten[0] -> 123456
40
+ # 2:s0.scan(/([A-Z]+)/).flatten[0] -> nil
41
+ # To achieve the required result you have to change the operational sequence
42
+ # like so
43
+ # 2:s0.scan(/([A-Z]+)/).flatten[0] -> CA
44
+ # 1.so.scan(/\/(.+)/).flatten[0] -> 123456
12
45
  class Calculator
13
46
 
14
47
  include Dsl
@@ -30,18 +63,24 @@ module Sycsvpro
30
63
  # if true add a sum row at the bottom of the out file
31
64
  attr_reader :add_sum_row
32
65
 
33
- # Creates a new Calculator. Options expects :infile, :outfile, :rows and
34
- # :columns. Optionally a header can be provided. The header can be
35
- # supplemented with additional column names that are generated due to a
36
- # arithmetic operation that creates new columns
66
+ # Creates a new Calculator. Optionally a header can be provided. The header
67
+ # can be supplemented with additional column names that are generated due
68
+ # to an arithmetic operation that creates new columns
37
69
  # :call-seq:
38
70
  # Sycsvpro::Calculator.new(infile: "in.csv",
39
71
  # outfile: "out.csv",
40
72
  # df: "%d.%m.%Y",
41
73
  # rows: "1,2,BEGINn3>20END",
42
74
  # header: "*,Count",
43
- # cols: "4:Count=c1+c2*2",
75
+ # cols: "4:c1+c2*2",
44
76
  # sum: true).execute
77
+ # infile:: File that contains the rows to be operated on
78
+ # outfile:: Result of the operations
79
+ # df:: Date format
80
+ # rows:: Row filter that indicates which rows to consider
81
+ # header:: Header of the columns
82
+ # cols:: Operations on the column values
83
+ # sum:: Indicate whether to add a sum row
45
84
  def initialize(options={})
46
85
  @infile = options[:infile]
47
86
  @outfile = options[:outfile]
@@ -59,6 +98,7 @@ module Sycsvpro
59
98
  def method_missing(id, *args, &block)
60
99
  return to_number(columns[$1.to_i]) if id =~ /c(\d+)/
61
100
  return to_date(columns[$1.to_i]) if id =~ /d(\d+)/
101
+ return columns[$1.to_i] if id =~ /s(\d+)/
62
102
  super
63
103
  end
64
104
 
@@ -68,7 +108,7 @@ module Sycsvpro
68
108
 
69
109
  File.open(outfile, 'w') do |out|
70
110
  File.open(infile).each_with_index do |line, index|
71
- next if line.chomp.empty?
111
+ next if line.chomp.empty? || unstring(line).chomp.split(';').empty?
72
112
 
73
113
  unless processed_header
74
114
  header_row = header.process(line.chomp)
@@ -115,7 +155,7 @@ module Sycsvpro
115
155
  def create_calculator(code)
116
156
  code.split(/,(?=\d+:)/).each do |operation|
117
157
  col, term = operation.split(':')
118
- term = "c#{col}#{term}" unless term =~ /^c\d+|^\[/
158
+ term = "c#{col}#{term}" if term =~ /^[+\-*\/%]/
119
159
  formulae[col] = term
120
160
  end
121
161
  end
data/lib/sycsvpro/dsl.rb CHANGED
@@ -2,6 +2,12 @@ require_relative 'row_filter'
2
2
 
3
3
  # Methods to be used in customer specific script files
4
4
  module Dsl
5
+
6
+ # Splits comma separated strings that contain commas within the value. Such
7
+ # values have to be enclosed between BEGIN and END
8
+ # Example:
9
+ # Year,c1+c2,c1=~/[A-Z]{1,2}/,Month
10
+ COMMA_SPLITTER_REGEX = /(?<=,|^)(BEGIN.*?END|\/.*?\/|.*?)(?=,|$)/i
5
11
 
6
12
  # read arguments provided at invocation
7
13
  # :call-seq:
@@ -85,6 +91,12 @@ module Dsl
85
91
  str.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
86
92
  end
87
93
 
94
+ # Retrieves the values scanned by a COMMA_SPLITTER_REGEX
95
+ def split_by_comma_regex(values)
96
+ values.scan(COMMA_SPLITTER_REGEX).flatten.each.
97
+ collect { |h| h.gsub(/BEGIN|END/, "") }
98
+ end
99
+
88
100
  private
89
101
 
90
102
  # Assigns values to keys that are used in rows and yielded to the block
@@ -11,14 +11,16 @@ module Sycsvpro
11
11
 
12
12
  # Header columns
13
13
  attr_reader :header_cols
14
+ # Columns that will be inserted into the header at the defined positions
15
+ attr_reader :insert_cols
16
+ # Positions where to insert the insert_cols
17
+ attr_reader :positions
14
18
 
15
19
  # Create a new header
16
- def initialize(header)
17
- unless header.nil? or header.empty?
18
- @header_cols = header.split(',')
19
- else
20
- @header_cols = []
21
- end
20
+ def initialize(header, options = {})
21
+ @header_cols = split_by_comma_regex(header || "")
22
+ @insert_cols = (options[:insert] || "").split(',')
23
+ @positions = options[:pos] || []
22
24
  end
23
25
 
24
26
  def method_missing(id, *args, &block)
@@ -28,7 +30,7 @@ module Sycsvpro
28
30
 
29
31
  # Returns the header
30
32
  def process(line, values = true)
31
- return "" if @header_cols.empty?
33
+ return "" if @header_cols.empty? && @insert_cols.empty?
32
34
  header_patterns = {}
33
35
  @row_cols = unstring(line).split(';')
34
36
  if @header_cols[0] == '*'
@@ -52,13 +54,14 @@ module Sycsvpro
52
54
  end
53
55
  end
54
56
  end
57
+ insert_header_cols
55
58
  header_patterns.each { |i,h| @header_cols.insert(i,h) }
56
59
  to_s
57
60
  end
58
61
 
59
62
  # Returns @header_cols without pattern
60
63
  def clear_header_cols
61
- @header_cols.flatten.select { |col| col !~ /^c\d+[=~+]{1,2}/ }
64
+ @header_cols.select { |col| col !~ /^c\d+[=~+]{1,2}/ }
62
65
  end
63
66
 
64
67
  # Returns the index of the column
@@ -66,11 +69,24 @@ module Sycsvpro
66
69
  clear_header_cols.index(value)
67
70
  end
68
71
 
72
+ # Returns the value of column number
73
+ def value_of(column)
74
+ clear_header_cols[column]
75
+ end
76
+
69
77
  # Returns the header
70
78
  def to_s
71
79
  clear_header_cols.join(';')
72
80
  end
73
81
 
82
+ private
83
+
84
+ def insert_header_cols
85
+ @header_cols.flatten!
86
+ positions.sort.each { |p| header_cols.insert(p, "") }
87
+ positions.each_with_index { |p,i| header_cols[p] = insert_cols[i] }
88
+ end
89
+
74
90
  end
75
91
 
76
92
  end