blob: 8654f3af7a1a8d6b5dbce95c24116d680378cfab (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
(ns csvtool.core
(:require [clojure.data.csv :as csv]
[clojure.pprint :refer [pprint]]
[clojure.tools.cli :refer [parse-opts]]
[clojure.java.io :as io])
(:gen-class))
(defn mk-mapping [row & keys]
(->> (zipmap keys row)
(#(dissoc % :ignore))))
(defn record-action [expr & rest]
(let [actions
{:set (fn [keyword value & rest]
(fn [record]
[(assoc record keyword value) rest]))
:mod (fn [keyword modf & rest]
(fn [record]
[(update record keyword modf) rest]))}]
(if (fn? expr)
(fn [record] [(expr record) rest])
(apply (get actions expr) rest))))
(defn record-actions [& exprs]
(fn [record]
(let [[next-record rest] ((apply record-action exprs) record)
next-action (if (empty? rest)
identity
(apply record-actions rest))]
(next-action next-record))))
(defn record-test [keyword pred]
(fn [record]
(if (contains? record keyword)
(pred (get record keyword))
nil)))
(defmacro mk-rule
[test & actions]
`(fn [record#]
(if (~test record#)
((~record-actions ~@actions) record#)
record#)))
(defmacro mk-chain
([] identity)
([rule]
`(mk-rule ~@rule))
([rule & next]
`(fn [record#]
((mk-chain ~@next)
((mk-chain ~rule) record#)))))
(defmacro defchain
[name & rules]
`(def ~name (mk-chain ~@rules)))
(def always (constantly true))
(defn matches [keyword pattern]
(fn [record]
(and (contains? record keyword)
(re-matches pattern (get record keyword)))))
(defn matches-any [keyword & patterns]
(fn [record]
(let [v (get record keyword)]
(some (fn [pattern] (re-matches pattern v)) patterns))))
(defchain dining
[(matches-any :desc
#"(?i)chipotle.*"
#"(?i)tappo- location.*")
:set :account2 "expenses:dining"])
(defchain default
[always :set :account2 "expenses:unknown"]
[always dining]
[(matches :desc #"(?i)key food.*")
:set :desc "Key Foods"
:set :account2 "expenses:groceries"]
[(matches :desc #"(?i)google \*cloud")
:set :account2 "expenses:services"])
(defn handle-record [row]
(let [record (mk-mapping row :date :postdate :desc :category :type :amount :memo)]
(default record)
))
(defn process-file [path]
(with-open [reader (io/reader path)]
(doall
(let [csv (csv/read-csv reader)
;; Skip header
data (drop 1 csv)
sample (take 10 data)
records (map handle-record sample)]
(pprint records)))))
(def cli-options
[["-f" "--file" :required "Input file"]
["-h" "--help"]])
(defn -main
[& args]
(pprint (parse-opts args cli-options))
(let [{:keys [options arguments errors summary]} (parse-opts args cli-options)]
(cond
(:file options) (process-file (:file options))
:else
(do (println "Failed")))))
|