diff --git a/README.md b/README.md index 06f0680..4ca6ffe 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,26 @@ You can also specify a day code (e.g. `wh` for a Work From Home (WFH) day): atoss-cli log -c wh -e "17:30" ``` +If you have a CSV file with your time entries, you can pass that as input too: + +```bash +atoss-cli log -f stunden.csv +``` + +The file must be in the following format: + +```csv +date,start,end,code +``` + +For example: + +```csv +25.10.2023,10:00,18:45,wh +``` + +The code can be optional, just make sure you have the correct number of columns. + If you are unsure about available day codes, you can always check ATOSS manually. To view the full list of options, call `atoss-cli -h` diff --git a/project.clj b/project.clj index 901c531..344a876 100644 --- a/project.clj +++ b/project.clj @@ -7,6 +7,7 @@ :min-lein-version "2.0.0" :dependencies [[org.clojure/clojure "1.11.1"] [org.clojure/tools.cli "1.0.206"] + [org.clojure/data.csv "1.0.1"] [etaoin "0.4.6"] [com.github.pmonks/spinner "2.0.190"] [clojure-term-colors "0.1.0"] diff --git a/src/atoss_cli/atoss.clj b/src/atoss_cli/atoss.clj index 0c510e9..7300735 100644 --- a/src/atoss_cli/atoss.clj +++ b/src/atoss_cli/atoss.clj @@ -24,26 +24,6 @@ (def time-pair-row {:css "div.slick-row"}) (def add-time-pair-btn {:css "ul.z-menupopup-content > li.z-menuitem > a.z-menuitem-content:first-of-type"}) -(defprotocol TimeSheetDay - (fmt-row [day])) - -(defrecord Day [date - day-of-week - comment - work-pattern - booking-code - day-code - start-time - start-time-correctness - end-time - end-time-correctness - time-logged - saldo - overtime] - TimeSheetDay (fmt-row - [day] - (apply format "%5s | %3s | %4s | %3s | %2s | %3s | %5s | %1s | %5s | %1s | %5s | %5s | %5s" (vals day)))) - (defn -max-row-cnt [driver] (-> driver @@ -153,22 +133,18 @@ (api/click date-input) (api/wait 1) (api/fill-active date) + (api/fill-active keys/enter) (api/wait 2) - (api/click update-btn) - (api/wait 1))) + (api/click date-input))) (defn create-time-pair-entry "Create a new time entry as a combination of day code and a time pair for a given day." [driver {day-code :day-code start :start-time end :end-time - verbosity :verbosity}] - (when (> verbosity 0) - (println (if (= day-code " ") - "No day code provided" - (str "Day code: " day-code)))) + date :date}] - (api/click driver date-input) + (set-date driver date) (dotimes [_i 4] (api/fill-active driver keys/tab)) (api/wait driver 3) ;; Do not touch waiters - if it is any less, the UI will not have enough time to update @@ -183,15 +159,9 @@ (api/fill-active keys/enter) (api/wait 2))) -(defn parse-month-table-rows - "Parse day records from month overview. Returns a collection of Days." - [driver] - (let [first-row 3 - last-row (- (-max-row-cnt driver) 3) - rows (range first-row last-row)] - (for [row rows] - (let [col-vals (for [col (range 0 13)] - (api/get-element-inner-html driver (-cell-selector row col))) - day (apply ->Day col-vals)] - day)))) - +(defn create-time-pair-entries + "Create time pair entries from a collection of time pairs." + [driver time-pairs] + (doseq [time-pair time-pairs] + (println "Creating time pair entry " time-pair) + (create-time-pair-entry driver time-pair))) diff --git a/src/atoss_cli/cli.clj b/src/atoss_cli/cli.clj index 84064fe..6a01c85 100644 --- a/src/atoss_cli/cli.clj +++ b/src/atoss_cli/cli.clj @@ -54,6 +54,7 @@ Work seamlessly with ATOSS time sheets.") :default "9:00"] ["-e" "--end-time TIME" "Work end time in the format HH:MM" :default "17:00"] + ["-f" "--file FILE" "Log times based on an input file."] ;; A non-idempotent option (:default is applied first) ["-v" nil "Verbosity level" :id :verbosity diff --git a/src/atoss_cli/core.clj b/src/atoss_cli/core.clj index 20e99e6..35b3b26 100644 --- a/src/atoss_cli/core.clj +++ b/src/atoss_cli/core.clj @@ -1,6 +1,8 @@ (ns atoss-cli.core "Entrypoint module for the ATOSS CLI." (:require + [clojure.java.io :as io] + [clojure.data.csv :as csv] [clojure.tools.cli :refer [parse-opts]] [clojure.term.colors :refer [bold green red]] [progress.indeterminate :as pi] @@ -21,12 +23,29 @@ (assoc opts :path-browser mac-chrome-path) opts))) -(defn log-time +(defn parse-time-pair-file + "Parse a CSV file containing time pairs and return a seq of maps." + [file-path] + (let [lines (-> file-path + io/reader + csv/read-csv)] + (try (->> lines + (filter #(> (count %) 1)) + (map #(let [[date start-time end-time day-code] %] + {:date date + :start-time start-time + :end-time end-time + :day-code day-code})) + (doall)) + (catch Exception e + (println (red "Error parsing time pair file: " (.getMessage e))) + (shutdown-agents))))) + +(defn log-time-single "Log a time pair for a given date." [{opts :options}] (let [driver (atoss/setup-driver (-maybe-inject-mac-chrome-path {:headless true})) config (config/load-in) - {date :date} opts session-opts (merge opts config)] (try (println (green "Logging time...")) @@ -34,12 +53,39 @@ (doto driver (atoss/login session-opts) (atoss/nav-to-time-correction) - (atoss/set-date date) (atoss/create-time-pair-entry session-opts) (atoss/logout session-opts) (atoss/end)) (pi/print - (green "Logged time for date: " date)) + (green "Logged time for date: " (:date opts))) + (shutdown-agents)) + + (catch Exception e + (-> e + (.getMessage) + (red) + (println)) + (atoss/end driver))))) + +(defn log-time-col + "Log a collection of time pairs." + [{opts :options}] + (let [driver (atoss/setup-driver (-maybe-inject-mac-chrome-path {:headless false})) + config (config/load-in) + session-opts (merge opts config) + {file :file} opts + time-pairs (parse-time-pair-file file)] + (try + (println (green "Logging time from file " file)) + (pi/animate! + (doto driver + (atoss/login session-opts) + (atoss/nav-to-time-correction) + (atoss/create-time-pair-entries time-pairs) + (atoss/logout session-opts) + (atoss/end)) + (pi/print + (green "Logged time from file " file)) (shutdown-agents)) (catch Exception e @@ -55,35 +101,29 @@ (let [config (config/load-in)] (atoss/browse config))) -;; FIXME: very brittle so disabled for now -(defn show-month-overview - "Display the current month overview in the terminal." - [{opts :options}] - (let [driver (atoss/setup-driver) - config (config/load-in)] - (doto driver - (atoss/login (merge opts config)) - (atoss/nav-to-month-overview)) - (let [days (atoss/parse-month-table-rows driver)] - (println (bold "Month overview:")) - (newline) - (doseq [day days] - (-> day - (atoss/fmt-row) - (println)))))) +(defn -config-cmd [args] + (let [[_cmd subcmd k v] args] + (cond + (= subcmd "init") (config/init) + (= subcmd "set") (config/set-val (keyword k) v) + :else (println "Unknown config command")))) + +(defn -log-cmd [{options :options :as opts}] + (cond + (options :file) (log-time-col opts) + :else (log-time-single opts))) (defn -main [& args] (let [{^Collection arguments :arguments summary :summary, options :options, :as opts} (parse-opts args cli/options) - [cmd subcmd k v] arguments] + [cmd _subcmd] arguments] (cond (options :version) (cli/print-project-ver) (options :help) (cli/print-help summary) - (= cmd "log") (log-time opts) + (= cmd "log") (-log-cmd opts) (= cmd "web") (web) - (and (= cmd "config") (= subcmd "init")) (config/init) - (and (= cmd "config") (= subcmd "set")) (config/set-val (keyword k) v) + (= cmd "config") (-config-cmd arguments) :else (cli/print-help summary)) (flush))) diff --git a/test/atoss_cli/atoss_test.clj b/test/atoss_cli/atoss_test.clj deleted file mode 100644 index 1532f24..0000000 --- a/test/atoss_cli/atoss_test.clj +++ /dev/null @@ -1,9 +0,0 @@ -(ns atoss-cli.atoss-test - (:require [clojure.test :refer [is testing]] - [atoss-cli.atoss :refer [->Day fmt-row]])) - -(def day (->Day "14.09" "Mi" "" "VGZ" "V" "wh" "09:00" "k" "18:00" "k" "8:30" "0:48" "")) - -(testing "fmt-row of a normal Day" - (is (= (fmt-row day) - "14.09 | Mi | | VGZ | V | wh | 09:00 | k | 18:00 | k | 8:30 | 0:48 | ")))