From fbb11fbcd8310cb4c9af444774b79ca064f470ac Mon Sep 17 00:00:00 2001
From: Anton Vakhrushev <anwinged@ya.ru>
Date: Sun, 17 Nov 2019 20:48:25 +0300
Subject: [PATCH] More consistent api

---
 assets/App.vue         | 40 +++++++++++++++++++---------------------
 spec/profile_spec.cr   |  2 +-
 src/dayoff.cr          | 16 ++++++++--------
 src/dayoff/entities.cr |  3 ++-
 src/dayoff/helpers.cr  | 12 ++++++++++++
 src/dayoff/profile.cr  | 17 ++++++-----------
 6 files changed, 48 insertions(+), 42 deletions(-)
 create mode 100644 src/dayoff/helpers.cr

diff --git a/assets/App.vue b/assets/App.vue
index cda5e32..4c9b488 100644
--- a/assets/App.vue
+++ b/assets/App.vue
@@ -1,14 +1,18 @@
 <template>
   <div id="app">
-    <section class="timer" v-bind:class="{ overtime: isOvertime }">
-      {{ time }}
+    <section
+      v-if="total_time"
+      class="timer"
+      v-bind:class="{ overtime: isOvertime }"
+    >
+      {{ total_time | str_time }}
     </section>
     <section class="actions">
       <a v-if="started" v-on:click.prevent="finish" href="#">Закончить</a>
       <a v-else v-on:click.prevent="start" href="#">Начать</a>
     </section>
-    <section class="today">
-      Сегодня {{ today_time }}
+    <section v-if="today_time" class="today">
+      Сегодня {{ today_time | str_time }}
     </section>
     <p class="profile-info">Профиль: {{ profileId }}</p>
   </div>
@@ -21,10 +25,8 @@ export default {
     return {
       profileId: null,
       started: false,
-      status: '',
-      hours: 0,
-      minutes: 0,
-      today: null,
+      total_time: null,
+      today_time: null,
     };
   },
   created() {
@@ -33,26 +35,16 @@ export default {
     setInterval(() => this.get_status(), 60 * 1000);
   },
   computed: {
-    time() {
-      const sign = this.isOvertime ? '+' : '';
-      return sign + this.hours + ':' + String(this.minutes).padStart(2, '0');
-    },
     isOvertime() {
-      return this.status === 'overtime';
+      return this.total_time && this.total_time.status === 'overtime';
     },
-    today_time() {
-      const sign = this.today.status === 'overtime' ? '+' : '';
-      return sign + this.today.hours + ':' + String(this.today.minutes).padStart(2, '0');      
-    }
   },
   methods: {
     get_status() {
       h.get_status(this.profileId).then(data => {
         this.started = data.started;
-        this.status = data.total.status;
-        this.hours = data.total.hours;
-        this.minutes = data.total.minutes;
-        this.today = data.today;
+        this.total_time = data.total.time;
+        this.today_time = data.today.time;
       });
     },
     start() {
@@ -62,6 +54,12 @@ export default {
       h.finish(this.profileId).then(() => this.get_status());
     },
   },
+  filters: {
+    str_time(time) {
+      const sign = time.status === 'overtime' ? '+' : '';
+      return sign + time.hours + ':' + String(time.minutes).padStart(2, '0');
+    },
+  },
 };
 </script>
 
diff --git a/spec/profile_spec.cr b/spec/profile_spec.cr
index 017f8ea..9c779a6 100644
--- a/spec/profile_spec.cr
+++ b/spec/profile_spec.cr
@@ -67,7 +67,7 @@ module Dayoff::Test
 
     it "can calc remaining time" do
       prof = create_profile
-      span = prof.remaining_time t(3, 12)
+      span = prof.total_status t(3, 12)
       expected = 8 * 3 - 10 * 2
       expected.should eq span.total_hours
     end
diff --git a/src/dayoff.cr b/src/dayoff.cr
index 6198e16..8b33b4f 100644
--- a/src/dayoff.cr
+++ b/src/dayoff.cr
@@ -17,10 +17,8 @@ def now
   Time.local(Time::Location.load("Europe/Moscow"))
 end
 
-def date_status(profile, date)
-  span = profile.date_status date
+def serialize_span(span : Time::Span)
   {
-    date:    date.to_s("%Y-%m-%d"),
     status:  span < Time::Span.zero ? STATUS_OVERTIME : STATUS_UPTIME,
     hours:   span.abs.total_hours.to_i32,
     minutes: span.abs.minutes.to_i32,
@@ -41,15 +39,17 @@ end
 
 get "/api/status" do |env|
   profile = app.profile Dayoff::ProfileId.new(env.get("profile_id").to_s)
-  rem_span = profile.remaining_time now
+  total_span = profile.total_status now
+  today_span = profile.date_status now
   data = {
     started: profile.started?,
     total:   {
-      status:  rem_span < Time::Span.zero ? STATUS_OVERTIME : STATUS_UPTIME,
-      hours:   rem_span.abs.total_hours.to_i32,
-      minutes: rem_span.abs.minutes.to_i32,
+      time: serialize_span total_span,
+    },
+    today: {
+      date: now.to_s("%Y-%m-%d"),
+      time: serialize_span today_span,
     },
-    today: date_status(profile, now),
   }
   env.response.content_type = "application/json"
   data.to_json
diff --git a/src/dayoff/entities.cr b/src/dayoff/entities.cr
index 421daa0..6b1ea86 100644
--- a/src/dayoff/entities.cr
+++ b/src/dayoff/entities.cr
@@ -70,7 +70,8 @@ module Dayoff
     end
 
     private def in_range_finished(from_time : Time, to_time : Time) : Time::Span
-      if @start <= to_time && finish! >= from_time
+      crossed = Helpers.crossed? @start, finish!, from_time, to_time
+      if crossed
         normalized_start = Math.max(@start, from_time)
         normalized_finish = Math.min(finish!, to_time)
         normalized_finish - normalized_start
diff --git a/src/dayoff/helpers.cr b/src/dayoff/helpers.cr
new file mode 100644
index 0000000..34e0db0
--- /dev/null
+++ b/src/dayoff/helpers.cr
@@ -0,0 +1,12 @@
+module Dayoff::Helpers
+  extend self
+
+  def zero_time : Time
+    location = Time::Location.load("Europe/Moscow")
+    Time.local(1, 1, 1, 0, 0, location: location)
+  end
+
+  def crossed?(s1 : Time, f1 : Time, s2 : Time, f2 : Time) : Bool
+    s1 <= f2 && f1 >= s2
+  end
+end
diff --git a/src/dayoff/profile.cr b/src/dayoff/profile.cr
index 757fe17..bcd7141 100644
--- a/src/dayoff/profile.cr
+++ b/src/dayoff/profile.cr
@@ -64,20 +64,15 @@ module Dayoff
       end
     end
 
-    private def zero_time : Time
-      location = Time::Location.load("Europe/Moscow")
-      Time.local(1, 1, 1, 0, 0, location: location)
-    end
-
-    def remaining_time(on_time : Time) : Time::Span
-      planned = get_planned zero_time, on_time
-      worked = get_worked zero_time, on_time
+    def total_status(on_time : Time) : Time::Span
+      planned = get_planned Helpers.zero_time, on_time
+      worked = get_worked Helpers.zero_time, on_time
       planned - worked
     end
 
-    def date_status(date : Time) : Time::Span
-      planned = get_planned date.at_beginning_of_day, date.at_end_of_day
-      worked = get_worked date.at_beginning_of_day, date
+    def date_status(on_time : Time) : Time::Span
+      planned = get_planned on_time.at_beginning_of_day, on_time.at_end_of_day
+      worked = get_worked on_time.at_beginning_of_day, on_time
       planned - worked
     end
   end