From a2d8855f28ee6c73646cf29f9aff8bd169461449 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Thu, 27 Feb 2020 22:16:39 +0300 Subject: [PATCH] Add statistics table --- assets/App.vue | 22 ++++++++++++++++++ assets/helpers.js | 17 +++++++++++++- spec/profile_spec.cr | 6 +++++ src/dayoff.cr | 20 +++++++++++++--- src/dayoff/entities.cr | 10 +++++++- src/dayoff/profile.cr | 52 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 5 deletions(-) diff --git a/assets/App.vue b/assets/App.vue index 69440fc..dde76a4 100644 --- a/assets/App.vue +++ b/assets/App.vue @@ -18,6 +18,16 @@

Профиль: {{ profileId }}

+ Статистика +
+ + + + + + +
{{ item.date }}{{ item.planned.total_minutes }}{{ item.worked.total_minutes }}
+
@@ -31,11 +41,14 @@ export default { started: false, total_time: null, today_time: null, + show_stat: false, + statistics: [], }; }, created() { this.profileId = h.extract_profile_id(); this.get_status(); + this.get_statistics(); setInterval(() => this.get_status(), 60 * 1000); }, methods: { @@ -46,6 +59,11 @@ export default { this.today_time = TimeSpan.fromObject(data.today.time); }); }, + get_statistics() { + h.get_statistics(this.profileId).then(data => { + this.statistics = data; + }); + }, start() { h.start(this.profileId).then(() => this.get_status()); }, @@ -85,4 +103,8 @@ export default { .profile-info { margin-top: 2em; } + +.stat-table { + display: inline-table; +} diff --git a/assets/helpers.js b/assets/helpers.js index 5f89bda..f686be9 100644 --- a/assets/helpers.js +++ b/assets/helpers.js @@ -32,4 +32,19 @@ async function finish(profileId) { }); } -export default { extract_profile_id, check_profile, get_status, start, finish }; +async function get_statistics(profileId) { + const response = await fetch('/api/statistics?profile_id=' + profileId, { + method: 'GET', + }); + const data = await response.json(); + return data; +} + +export default { + extract_profile_id, + check_profile, + get_status, + start, + finish, + get_statistics, +}; diff --git a/spec/profile_spec.cr b/spec/profile_spec.cr index 9c779a6..4d31522 100644 --- a/spec/profile_spec.cr +++ b/spec/profile_spec.cr @@ -77,5 +77,11 @@ module Dayoff::Test span = prof.date_status t(1, 15) span.total_hours.should eq 3 end + + it "can get statistics" do + prof = create_profile + stat = prof.statistics t(3, 12) + stat.size.should eq 3 + end end end diff --git a/src/dayoff.cr b/src/dayoff.cr index 8b33b4f..7444988 100644 --- a/src/dayoff.cr +++ b/src/dayoff.cr @@ -19,9 +19,10 @@ end def serialize_span(span : Time::Span) { - status: span < Time::Span.zero ? STATUS_OVERTIME : STATUS_UPTIME, - hours: span.abs.total_hours.to_i32, - minutes: span.abs.minutes.to_i32, + status: span < Time::Span.zero ? STATUS_OVERTIME : STATUS_UPTIME, + hours: span.abs.total_hours.to_i32, + minutes: span.abs.minutes.to_i32, + total_minutes: span.abs.total_minutes.to_i32, } end @@ -55,6 +56,19 @@ get "/api/status" do |env| data.to_json end +get "/api/statistics" do |env| + profile = app.profile Dayoff::ProfileId.new(env.get("profile_id").to_s) + data = profile.statistics now + mapped = data.map do |v| + { + date: v.date.to_s("%Y-%m-%d"), + planned: serialize_span(v.planned), + worked: serialize_span(v.worked), + } + end + mapped.to_json +end + get "/" do render "public/index.ecr" end diff --git a/src/dayoff/entities.cr b/src/dayoff/entities.cr index 6b1ea86..b629d7f 100644 --- a/src/dayoff/entities.cr +++ b/src/dayoff/entities.cr @@ -23,9 +23,13 @@ module Dayoff def initialize(@date : Time, @hours : Int32) end + def hours_as_span : Time::Span + Time::Span.new(hours: @hours, minutes: 0, seconds: 0) + end + def in_range(from_time : Time, to_time : Time) : Time::Span if @date >= from_time && @date < to_time - Time::Span.new(hours: @hours, minutes: 0, seconds: 0) + hours_as_span else Time::Span.zero end @@ -61,6 +65,10 @@ module Dayoff !@finish.nil? end + def get_span(to_time : Time) : Time::Span + in_range @start, to_time + end + def in_range(from_time : Time, to_time : Time) : Time::Span if finished? in_range_finished from_time, to_time diff --git a/src/dayoff/profile.cr b/src/dayoff/profile.cr index bcd7141..3360735 100644 --- a/src/dayoff/profile.cr +++ b/src/dayoff/profile.cr @@ -12,6 +12,23 @@ module Dayoff end end + class DayStatRecord + getter date + getter planned + getter worked + + def initialize(@date : Time, @planned : Time::Span, @worked : Time::Span) + end + + def add_planned(v : Time::Span) + @planned += v + end + + def add_worked(v : Time::Span) + @worked += v + end + end + class Profile @pdates = [] of PlannedDate @wrecords = [] of WorkRecord @@ -75,5 +92,40 @@ module Dayoff worked = get_worked on_time.at_beginning_of_day, on_time planned - worked end + + def statistics(on_time : Time) : Array(DayStatRecord) + dates = {} of String => DayStatRecord + + @pdates.each do |pd| + key = pd.date.to_s("%Y-%m-%d") + dates[key] = DayStatRecord.new( + pd.date, + pd.hours_as_span, + Time::Span.zero + ) + end + + @wrecords.each do |wr| + key = wr.start.to_s("%Y-%m-%d") + if dates.has_key? key + dates[key].add_worked wr.in_range(Helpers.zero_time, on_time) + else + dates[key] = DayStatRecord.new( + wr.start, + Time::Span.zero, + wr.in_range(Helpers.zero_time, on_time) + ) + end + end + + date_keys = dates.keys.sort!.reverse! + + result = [] of DayStatRecord + date_keys.each do |k| + result.push dates[k] + end + + result + end end end