Skip to content

Commit 1ee2d59

Browse files
committed
finish 4 hw
1 parent 51c480a commit 1ee2d59

2 files changed

Lines changed: 160 additions & 2 deletions

File tree

otus-06/project.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
:url "https://www.eclipse.org/legal/epl-2.0/"}
66
:dependencies [[org.clojure/clojure "1.11.1"]
77
[aero "1.1.6"]]
8+
:main otus-06.homework
89

9-
:repl-options {:init-ns otus-06.core})
10+
:repl-options {:init-ns otus-06.homework})

otus-06/src/otus_06/homework.clj

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
(ns otus-06.homework)
1+
(ns otus-06.homework
2+
(:require [clojure.string :as str]
3+
[clojure.java.io :as io]))
24

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

9597

9698
;; Файлы находятся в папке otus-06/resources/homework
99+
100+
(def cust-file "resources/homework/cust.txt")
101+
(def prod-file "resources/homework/prod.txt")
102+
(def sales-file "resources/homework/sales.txt")
103+
104+
(def table-schema
105+
{:cust {:file cust-file :id 0 :name 1 :address 2 :phone 3}
106+
:prod {:file prod-file :id 0 :name 1 :cost 2}
107+
:sales {:file sales-file :id 0 :cust-id 1 :prod-id 2 :count 3}})
108+
109+
(defn map-line [line schema]
110+
(into {} (for [[key value] (dissoc schema :file)]
111+
[key (nth line value)])))
112+
113+
(defn clear-terminal []
114+
(print "\033c"))
115+
116+
(defn parse-line [line]
117+
(str/split line #"\|"))
118+
119+
(defn for-each-line [file cb]
120+
(with-open [rdr (io/reader file)]
121+
(doseq [[index line] (map-indexed vector (line-seq rdr))]
122+
(let [line (parse-line line)]
123+
(cb line index)))))
124+
125+
(defn format-row [row index]
126+
(->> row
127+
(mapv #(format "\"%s\"" %))
128+
(str/join ", ")
129+
(format "%d: [%s]" (inc index))))
130+
131+
(defn print-table [file]
132+
(for-each-line file (fn [line index]
133+
(-> (format-row line index)
134+
(println)))))
135+
136+
(defn load-table [table]
137+
(let [{:keys [file] :as schema} (table table-schema)
138+
all-ids (atom [])
139+
by-id (atom {})]
140+
(for-each-line file (fn [line _] ;; do we have a way to pass callback with only one argument?
141+
(let [[id] line
142+
data (map-line line schema)]
143+
(swap! all-ids conj id)
144+
(swap! by-id assoc id data))))
145+
{:all-ids @all-ids
146+
:by-id @by-id}))
147+
148+
(defn load-tables []
149+
{:cust (load-table :cust)
150+
:prod (load-table :prod)
151+
:sales (load-table :sales)})
152+
153+
(defn find-by-id [db table id]
154+
(get-in db [table :by-id id]))
155+
156+
(defn find-by-name [db table name]
157+
(let [table (get-in db [table :by-id])
158+
[[id data]] (filter (fn [[_key value]] (= name (:name value))) table)]
159+
{:id id :data data}))
160+
161+
(defn calc-total-sales-for-customer [db cust-id]
162+
(let [sales (get-in db [:sales :by-id])
163+
sales-to-customer (filter (fn [[_key value]] (= cust-id (:cust-id value))) sales)]
164+
(reduce (fn [acc [_key value]]
165+
(let [count (Integer/parseInt (get value :count "0"))
166+
product (get-in db [:prod :by-id (:prod-id value)])
167+
cost (Float/parseFloat (get product :cost "0.00"))]
168+
(+ acc (* count cost))))
169+
0 sales-to-customer)))
170+
171+
(defn calc-total-count-for-product [db prod-id]
172+
(let [sales (get-in db [:sales :by-id])
173+
product-sales (filter (fn [[_key value]] (= prod-id (:prod-id value))) sales)]
174+
(reduce (fn [acc [_key value]]
175+
(let [count (Integer/parseInt (get value :count "0"))]
176+
(+ acc count)))
177+
0 product-sales)))
178+
179+
(defn on-exit [running?]
180+
(println "Goodbye")
181+
(dosync (ref-set running? false)))
182+
183+
(defn on-display-customer-table []
184+
(print-table cust-file))
185+
186+
(defn on-display-product-table []
187+
(print-table prod-file))
188+
189+
(defn on-display-sales-table []
190+
(let [db (load-tables)]
191+
(doseq [[index id] (map-indexed vector (get-in db [:sales :all-ids]))
192+
:let [{:keys [cust-id prod-id count]} (find-by-id db :sales id)
193+
{cust-name :name} (find-by-id db :cust cust-id)
194+
{prod-name :name} (find-by-id db :prod prod-id)]]
195+
(println (format-row [cust-name prod-name count] index)))))
196+
197+
(defn on-display-total-sales-for-customer []
198+
(let [db (load-tables)]
199+
(println "Enter customer name:")
200+
(let [customer-name (read-line)
201+
{customer-id :id customer :data} (find-by-name db :cust customer-name)]
202+
(if customer
203+
(let [total (calc-total-sales-for-customer db customer-id)]
204+
(println (format "Total Sales for Customer %s: $%.2f" customer-name total)))
205+
(println (format "Customer %s not found" customer-name))))))
206+
207+
(defn on-display-total-count-for-product []
208+
(let [db (load-tables)]
209+
(println "Enter product name:")
210+
(let [product-name (read-line)
211+
{product-id :id product :data} (find-by-name db :prod product-name)]
212+
(if product
213+
(let [count (calc-total-count-for-product db product-id)]
214+
(println (format "Total Count of Product %s: %d" product-name count)))
215+
(println (format "Product %s not found" product-name))))))
216+
217+
(defn render-menu [menu]
218+
(clear-terminal)
219+
(println "*** Sales Menu ***")
220+
(println "==================")
221+
(doseq [[index item] (map-indexed vector menu)]
222+
(println (str (inc index) ". " (:title item))))
223+
(println "Enter an option? "))
224+
225+
(defn get-user-input []
226+
(let [input (read-line)]
227+
(try
228+
(Integer/parseInt input)
229+
(catch Exception e
230+
(println "Invalid input")
231+
-1))))
232+
233+
(defn start-app
234+
"Displaying main menu and processing user choices."
235+
[]
236+
(let [running? (ref true)
237+
menu [{:title "Display Customer Table" :action on-display-customer-table}
238+
{:title "Display Product Table" :action on-display-product-table}
239+
{:title "Display Sales Table" :action on-display-sales-table}
240+
{:title "Total Sales for Customer" :action on-display-total-sales-for-customer}
241+
{:title "Total Count for Product" :action on-display-total-count-for-product}
242+
{:title "Exit" :action (partial on-exit running?)}]]
243+
(render-menu menu)
244+
(while @running?
245+
(let [choice (get-user-input)
246+
item (get menu (dec choice))
247+
action (get item :action (fn []))]
248+
(action)))))
249+
250+
(defn -main
251+
"Main function calling app."
252+
[& _args]
253+
(start-app))

0 commit comments

Comments
 (0)