(ns auto-ap.parse.csv (:require [auto-ap.parse.util :as u] [clojure.data.csv :as csv] [clojure.java.io :as io] [clojure.string :as str] [clojure.tools.logging :as log])) (defn determine [[header]] (log/info "Importing with header" header) (let [csv-type (cond (str/includes? (second header) "Customer's PO No.") :mama-lus (str/includes? (str header) "Ship-To Number") :sysco-style-2 (str/includes? (str header) "Closed Date") :sysco-style-1 (str/includes? (str header) "Business Unit") :mission (str/includes? (str header) "Document Number") :philz (str/includes? (str header) "DISCOUNT_MESSAGE") :wismettac (str/includes? (str header) "Status") :ledyard :else nil)] (log/info "csv type was determined to be" csv-type) csv-type)) (defmulti parse-csv determine :default #_{:clj-kondo/ignore [:unused-binding]} (fn default [rows] nil)) (defn parse-date-fallover [d fmts] (when-let [valid-fmt (->> fmts (filter (fn [f] (try (u/parse-value :clj-time f d) (catch Exception _ nil)) )) (first))] (u/parse-value :clj-time valid-fmt d))) (defmethod parse-csv :sysco-style-1 [rows] (let [header (first rows)] (transduce (comp (drop 1) (map (fn [row] (into {} (map vector header row)))) (filter (fn [row] (and (not (str/blank? (get row "Invoice Date"))) (not (str/blank? (get row "Orig Amt"))) (not (str/blank? (get row "Inv #")))))) (map (fn [row] {:vendor-code nil :customer-identifier nil :invoice-number (get row "Inv #") :date (parse-date-fallover (get row "Invoice Date") ["M/d/yyyy"]) :total (str/replace (get row "Orig Amt") #"[,\$]" "") :text (str/join " " (vals row)) :full-text (str/join " " (vals row))}))) conj [] rows))) (defmethod parse-csv :sysco-style-2 [rows] (let [header (first rows)] (transduce (comp (drop 1) (map (fn [row] (into {} (map vector header row)))) (map (fn [row] {:vendor-code nil :customer-identifier (str (get row "Ship-To Name") " " (or (get row "Ship-To Number") (get row "\"Ship-To Number\""))) :invoice-number (str/trim (get row "Invoice Number")) :date (parse-date-fallover (get row "Invoice Date") ["yyyy-MM-dd" "M/d/yyyy"]) :total (str/replace (get row "Original Amount") #"[,\$]" "") :text (str/join " " (vals row)) :full-text (str/join " " (vals row))}))) conj [] rows))) (defmethod parse-csv :mama-lus [rows] (transduce (comp (drop 1) (map (fn [[_ po-number _ invoice-number invoice-date customer value :as row]] {:vendor-code "Mama Lu's Foods" :customer-identifier customer :invoice-number (str po-number "-" invoice-number ) :date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"]) :total (str/replace value #"," "") :text (str/join " " row) :full-text (str/join " " row)}))) conj [] rows)) (defmethod parse-csv :mission [rows] (transduce (comp (drop 1) (map (fn [[ po-number _ invoice-number invoice-date customer value :as row]] {:vendor-code "Mama Lu's Foods" :customer-identifier customer :invoice-number (str po-number "-" invoice-number ) :date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"]) :total (str/replace value #"," "") :text (str/join " " row) :full-text (str/join " " row)}))) conj [] rows)) (defmethod parse-csv :philz [rows] (transduce (comp (filter (fn [[_ _ _ _ _ status _ _ _ ]] (= status "Billed"))) (map (fn [[dt _ doc-number name _ _ _ _ amount :as row]] {:vendor-code "PHILZ COFFEE, INC" :customer-identifier name :invoice-number doc-number :date (some-> dt not-empty (parse-date-fallover ["MM/dd/yyyy"])) :total (str/replace amount #"," "") :text (str/join " " row) :full-text (str/join " " row)})) ) conj [] (drop 1 rows))) (defmethod parse-csv :wismettac [rows] (transduce (comp (map (fn [[inv_number inv_dt total :as row]] {:vendor-code "Wismettac" :invoice-number inv_number :date (some-> inv_dt not-empty (parse-date-fallover ["MM/dd/yyyy"])) :total (str/replace total #"," "") :text (str/join " " row) :full-text (str/join " " row)})) ) conj [] (drop 1 rows))) (defmethod parse-csv :ledyard [rows] (transduce (comp (map (fn [[invoice-number date due amount standard :as row]] {:vendor-code "Performance Food Group - LEDYARD" :invoice-number invoice-number :date (some-> date not-empty (parse-date-fallover ["MM/dd/yyyy"])) :due (some-> due not-empty (parse-date-fallover ["MM/dd/yyyy"])) :total (str/replace amount #"[\$,]" "") :text (str/join " " row) :full-text (str/join " " row)})) ) conj [] (drop 1 rows))) #_{:clj-kondo/ignore [:unused-binding]} (defmethod parse-csv nil [rows] nil) (defn parse-file [file _] (with-open [reader (io/reader file)] (let [rows (csv/read-csv reader :separator \,)] (parse-csv rows))))