Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion otus-06/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.11.1"]
[aero "1.1.6"]]
:main otus-06.homework

:repl-options {:init-ns otus-06.core})
:repl-options {:init-ns otus-06.homework})
159 changes: 158 additions & 1 deletion otus-06/src/otus_06/homework.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
(ns otus-06.homework)
(ns otus-06.homework
(:require [clojure.string :as str]
[clojure.java.io :as io]))

;; Загрузить данные из трех файлов на диске.
;; Эти данные сформируют вашу базу данных о продажах.
Expand Down Expand Up @@ -94,3 +96,158 @@


;; Файлы находятся в папке otus-06/resources/homework

(def cust-file "resources/homework/cust.txt")
(def prod-file "resources/homework/prod.txt")
(def sales-file "resources/homework/sales.txt")

(def table-schema
{:cust {:file cust-file :title "customer" :id 0 :name 1 :address 2 :phone 3}
:prod {:file prod-file :title "product" :id 0 :name 1 :cost 2}
:sales {:file sales-file :title "sales" :id 0 :cust-id 1 :prod-id 2 :count 3}})

(defn map-line [line schema]
(into {} (for [[key value] (dissoc schema :file :title)]
[key (nth line value)])))

(defn clear-terminal []
(print "\033c"))

(defn parse-line [line]
(str/split line #"\|"))

(defn format-row [row index]
(->> row
(mapv #(format "\"%s\"" %))
(str/join ", ")
(format "%d: [%s]" (inc index))))

(defn print-table [file]
(with-open [rdr (io/reader file)]
(->> (line-seq rdr)
(map parse-line)
(map-indexed vector)
(mapv (fn [[index line]]
(format-row line index)))
(str/join "\n")
(println))))

(defn load-table [table]
(let [{:keys [file] :as schema} (table table-schema)]
(with-open [rdr (io/reader file)]
(->> (line-seq rdr)
(map parse-line)
(mapv #(map-line % schema))
(reduce (fn [acc value]
(-> acc
(update :by-id assoc (:id value) value)
(update :all-ids conj (:id value))))
{:by-id {} :all-ids []})))))

(defn load-tables []
{:cust (load-table :cust)
:prod (load-table :prod)
:sales (load-table :sales)})

(defn find-by-id [db table id]
(get-in db [table :by-id id]))

(defn find-by-name [db table name]
(let [table (get-in db [table :by-id])
[[id data]] (filter (fn [[_key value]] (= name (:name value))) table)]
{:id id :data data}))

(defn calc-total-sales-for-customer [db cust-id]
(let [sales (get-in db [:sales :by-id])
sales-to-customer (filter (fn [[_key value]] (= cust-id (:cust-id value))) sales)]
(reduce (fn [acc [_key value]]
(let [count (Integer/parseInt (get value :count "0"))
product (get-in db [:prod :by-id (:prod-id value)])
cost (Float/parseFloat (get product :cost "0.00"))]
(+ acc (* count cost))))
0 sales-to-customer)))

(defn calc-total-count-for-product [db prod-id]
(let [sales (get-in db [:sales :by-id])
product-sales (filter (fn [[_key value]] (= prod-id (:prod-id value))) sales)]
(reduce (fn [acc [_key value]]
(let [count (Integer/parseInt (get value :count "0"))]
(+ acc count)))
0 product-sales)))

(defn on-exit [running?]
(println "Goodbye")
(dosync (ref-set running? false)))

(defn on-display-customer-table []
(print-table cust-file))

(defn on-display-product-table []
(print-table prod-file))

(defn on-display-sales-table []
(let [db (load-tables)]
(doseq [[index id] (map-indexed vector (get-in db [:sales :all-ids]))
:let [{:keys [cust-id prod-id count]} (find-by-id db :sales id)
{cust-name :name} (find-by-id db :cust cust-id)
{prod-name :name} (find-by-id db :prod prod-id)]]
(println (format-row [cust-name prod-name count] index)))))

(defn handle-entity-metric [entity-type metric-calc-fn]
(let [db (load-tables)
title (str/capitalize (get-in table-schema [entity-type :title]))]
(println (format "Enter %s name:" title))
(let [entity-name (read-line)
{entity-id :id entity :data} (find-by-name db entity-type entity-name)]
(if entity
(let [result (metric-calc-fn db entity-id)]
(println (format "Total for %s %s: %s"
title entity-name result)))
(println (format "%s %s not found" title entity-name))))))

(defn on-display-total-sales-for-customer []
(handle-entity-metric :cust (comp #(format "%.2f" %) calc-total-sales-for-customer)))

(defn on-display-total-count-for-product []
(handle-entity-metric :prod calc-total-count-for-product))

(defn on-no-such-action []
(println "Not valid option. Please enter a valid one."))

(defn render-menu [menu]
(clear-terminal)
(println "*** Sales Menu ***")
(println "==================")
(doseq [[index item] (map-indexed vector menu)]
(println (str (inc index) ". " (:title item))))
(println "Enter an option? "))

(defn get-user-input []
(let [input (read-line)]
(try
(Integer/parseInt input)
(catch Exception _
(println "Invalid input")
-1))))

(defn start-app
"Displaying main menu and processing user choices."
[]
(let [running? (ref true)
menu [{:title "Display Customer Table" :action on-display-customer-table}
{:title "Display Product Table" :action on-display-product-table}
{:title "Display Sales Table" :action on-display-sales-table}
{:title "Total Sales for Customer" :action on-display-total-sales-for-customer}
{:title "Total Count for Product" :action on-display-total-count-for-product}
{:title "Exit" :action (partial on-exit running?)}]]
(render-menu menu)
(while @running?
(let [choice (get-user-input)
item (get menu (dec choice))
action (get item :action on-no-such-action)]
(action)))))

(defn -main
"Main function calling app."
[& _args]
(start-app))