|
61 | 61 | "Splits an daily interval into off-hours.
|
62 | 62 | Necessary for weekdays where oncall is not charged
|
63 | 63 | during working hours (9am - 6pm)"
|
64 |
| - [start end] |
65 |
| - (let [nine-am (t/plus-hours (t/truncated-to start unit/days) 9) |
66 |
| - six-pm (t/plus-hours (t/truncated-to start unit/days) 18)] |
| 64 | + [start end half-day-ptos] |
| 65 | + (let [is-pto? (and half-day-ptos |
| 66 | + (half-day-ptos (t/to-local-date start)) |
| 67 | + (t/plus-hours (t/truncated-to start unit/days) 14)) |
| 68 | + start-work (or is-pto? |
| 69 | + (t/plus-hours (t/truncated-to start unit/days) 9)) |
| 70 | + end-work (t/plus-hours (t/truncated-to start unit/days) 18)] |
67 | 71 | (filter some? [;; period between 0-9
|
68 |
| - (when (t/is-after nine-am start) |
| 72 | + (when (t/is-after start-work start) |
69 | 73 | {:start start
|
70 |
| - :end (if (t/is-after end nine-am) |
71 |
| - nine-am |
72 |
| - end)}) |
| 74 | + :pto is-pto? |
| 75 | + :end (if (t/is-after end start-work) |
| 76 | + start-work |
| 77 | + end)}) |
73 | 78 | ;; period between 18-24
|
74 |
| - (when (t/is-after end six-pm) |
75 |
| - {:start (if (t/is-after start six-pm) |
| 79 | + (when (t/is-after end end-work) |
| 80 | + {:start (if (t/is-after start end-work) |
76 | 81 | start
|
77 |
| - six-pm) |
78 |
| - :end end})]))) |
| 82 | + end-work) |
| 83 | + :pto is-pto? |
| 84 | + :end end})]))) |
79 | 85 |
|
80 | 86 | (defn interval->dates
|
81 | 87 | "Splits a several-days interval into consecutive intervals
|
82 | 88 | of several hours during each day.
|
83 | 89 | Differenciates workdays and weekends/holidays"
|
84 |
| - [{:keys [start end] :as schedule}] |
85 |
| - (loop [current-dt start |
86 |
| - time-rows []] |
87 |
| - (let [next-dt (beginning-of-next-day current-dt)] |
88 |
| - (if (t/is-after end current-dt) |
89 |
| - ;; loop into next day |
90 |
| - (if (or (dow/equals dow/saturday (dow/from current-dt)) |
91 |
| - (dow/equals dow/sunday (dow/from current-dt)) |
92 |
| - (h/holiday? current-dt)) |
93 |
| - (recur next-dt |
94 |
| - (concat time-rows |
95 |
| - (list (merge schedule |
96 |
| - {:start current-dt |
97 |
| - :end (if (t/is-after end next-dt) |
98 |
| - next-dt |
99 |
| - end)} )))) |
100 |
| - (recur next-dt |
101 |
| - (concat time-rows |
102 |
| - (map (partial merge schedule) |
103 |
| - (weekday-times current-dt |
104 |
| - (if (t/is-after end next-dt) |
105 |
| - next-dt |
106 |
| - end)))))) |
107 |
| - ;; exit loop |
108 |
| - time-rows)))) |
| 90 | + [{:keys [start end user] :as schedule} ptos] |
| 91 | + (let [{full-day-ptos 1.0 |
| 92 | + half-day-ptos 0.5} (->> ptos |
| 93 | + ;; TODO transducer |
| 94 | + (filter #(= (:summary user) (:employee %))) |
| 95 | + (map #(select-keys % [:days :date])) |
| 96 | + (map #(assoc % :date (.toInstant (:date %)))) |
| 97 | + (map #(assoc % :date (t/of-instant (:date %) (t/get-zone (t/now))))) |
| 98 | + (map #(assoc % :date (t/to-local-date (:date %)))) |
| 99 | + (group-by :days) |
| 100 | + (map (fn [[k v]] [k (set (map :date v))])) |
| 101 | + (into {}))] |
| 102 | + (loop [current-dt start |
| 103 | + time-rows []] |
| 104 | + (let [next-dt (beginning-of-next-day current-dt) |
| 105 | + full-day-pto? (and full-day-ptos |
| 106 | + (full-day-ptos (t/to-local-date current-dt)))] |
| 107 | + (if (t/is-after end current-dt) |
| 108 | + ;; loop into next day |
| 109 | + (if (or (dow/equals dow/saturday (dow/from current-dt)) |
| 110 | + (dow/equals dow/sunday (dow/from current-dt)) |
| 111 | + (h/holiday? current-dt) |
| 112 | + full-day-pto?) |
| 113 | + (recur next-dt |
| 114 | + (concat time-rows |
| 115 | + (list (merge schedule |
| 116 | + {:start current-dt |
| 117 | + :pto full-day-pto? |
| 118 | + :end (if (t/is-after end next-dt) |
| 119 | + next-dt |
| 120 | + end)} )))) |
| 121 | + (recur next-dt |
| 122 | + (concat time-rows |
| 123 | + (map (partial merge schedule) |
| 124 | + (weekday-times current-dt |
| 125 | + (if (t/is-after end next-dt) |
| 126 | + next-dt |
| 127 | + end) |
| 128 | + half-day-ptos))))) |
| 129 | + ;; exit loop |
| 130 | + time-rows))))) |
109 | 131 |
|
110 | 132 | (defn interval->hours
|
111 | 133 | [{:keys [start end]}]
|
|
115 | 137 |
|
116 | 138 | (defn date->row
|
117 | 139 | "Given an interval, generates the corresponding row for the oncall sheet"
|
118 |
| - [{:keys [start end] :as interval}] |
| 140 | + [{:keys [start end pto] :as interval}] |
119 | 141 | (let [time-format (dtf/of-pattern "HH:mm")
|
120 | 142 | date-format (dtf/of-pattern "dd/MM/YYYY")
|
121 |
| - start-str (dtf/format time-format start) |
122 |
| - end-str (dtf/format time-format end)] |
| 143 | + start-str (dtf/format time-format start) |
| 144 | + end-str (dtf/format time-format end)] |
123 | 145 | (list (dtf/format date-format start)
|
124 | 146 | start-str
|
125 | 147 | (if (= end-str "00:00")
|
126 | 148 | "24:00"
|
127 | 149 | end-str)
|
128 |
| - (interval->hours interval)))) |
| 150 | + (interval->hours interval) |
| 151 | + (when pto "PTO")))) |
129 | 152 |
|
130 | 153 |
|
131 | 154 | (comment
|
132 |
| - (->> {:start "2021-02-05T00:34:00-04:00" |
133 |
| - :user {:id "123"} |
134 |
| - :end "2021-02-07T12:00:00-04:00"} |
135 |
| - parse-dates |
136 |
| - trim-dates |
137 |
| - interval->dates |
138 |
| - (map date->row)) |
| 155 | + (require '[rock-n-call.common.pto :refer [file->maps]]) |
| 156 | + (def ptos (file->maps "example2.xlsx")) |
| 157 | + (->> {:start "2021-07-01T00:34:00-02:00" |
| 158 | + :user {:id "123" :summary "Remy Rojas"} |
| 159 | + :end "2021-07-14T12:00:00-02:00"} |
| 160 | + parse-dates |
| 161 | + trim-dates |
| 162 | + (#(interval->dates % ptos)) |
| 163 | + (map date->row)) |
139 | 164 | )
|
0 commit comments