From 5648c942c13df79c2b9aeecb20bd5de2c6864abb Mon Sep 17 00:00:00 2001 From: weijiuqiao <59040746+weijiuqiao@users.noreply.github.com> Date: Sun, 2 Apr 2023 01:15:36 +0800 Subject: [PATCH] Calendar view: add options to change start time of days (#10254) Can be set for example to 04:00 to see after-midnight readings with those of the previous evening in day timeline. Also fix possible shifts in day start when crossing DST changes with prev/next. Also fix sorting (by reverse reading duration) of books at top --- plugins/statistics.koplugin/calendarview.lua | 64 ++++++++++++++---- plugins/statistics.koplugin/main.lua | 71 +++++++++++++++++--- 2 files changed, 111 insertions(+), 24 deletions(-) diff --git a/plugins/statistics.koplugin/calendarview.lua b/plugins/statistics.koplugin/calendarview.lua index b16c86fb7..8ed747315 100644 --- a/plugins/statistics.koplugin/calendarview.lua +++ b/plugins/statistics.koplugin/calendarview.lua @@ -683,8 +683,12 @@ function CalendarDayView:setupView() self.is_current_day = now >= self.day_ts and now < self.day_ts + 86400 if self.is_current_day then local date = os.date("*t", now) - self.current_day_hour = date.hour - self.current_hour_second = date.min * 60 + date.sec + self.current_day_hour = date.hour - (self.reader_statistics.settings.calendar_day_start_hour or 0) + self.current_hour_second = date.min * 60 + date.sec - (self.reader_statistics.settings.calendar_day_start_minute or 0) * 60 + if self.current_hour_second < 0 then + self.current_day_hour = self.current_day_hour - 1 + self.current_hour_second = 3600 + self.current_hour_second + end end self.kv_pairs = self.reader_statistics:getBooksFromPeriod(self.day_ts, self.day_ts + 86400) @@ -695,7 +699,7 @@ function CalendarDayView:setupView() end kv.checked = true end - table.sort(self.kv_pairs, function(a,b) return a[2] > b[2] end) --sort by value + table.sort(self.kv_pairs, function(a,b) return a.duration > b.duration end) --sort by value self.title = self:title_callback() self.show_page = 1 @@ -761,19 +765,47 @@ function CalendarDayView:goToPage(page) end function CalendarDayView:onNextPage() - if not self:nextPage() and self.day_ts + 86400 < os.time() then - -- go to next day - self.day_ts = self.day_ts + 86400 - self:setupView() + if not self:nextPage() and self.day_ts + 82800 < os.time() then + local current_day_ts = self.day_ts - (self.reader_statistics.settings.calendar_day_start_hour or 0) * 3600 + - (self.reader_statistics.settings.calendar_day_start_minute or 0) * 60 + local next_day_ts = current_day_ts + 86400 + 10800 -- make sure it's the next day + local next_day_date = os.date("*t", next_day_ts) + next_day_ts = os.time({ + year = next_day_date.year, + month = next_day_date.month, + day = next_day_date.day, + hour = 0, + min = 0, + }) + local current_day_length = next_day_ts - current_day_ts + if self.day_ts + current_day_length < os.time() then + -- go to next day + self.day_ts = self.day_ts + current_day_length + self:setupView() + end end return true end function CalendarDayView:onPrevPage() - if not self:prevPage() and self.day_ts - 86400 >= self.min_ts then - -- go to previous day - self.day_ts = self.day_ts - 86400 - self:setupView() + if not self:prevPage() and self.day_ts - 82800 >= self.min_ts then + local current_day_ts = self.day_ts - (self.reader_statistics.settings.calendar_day_start_hour or 0) * 3600 + - (self.reader_statistics.settings.calendar_day_start_minute or 0) * 60 + local previous_day_ts = current_day_ts - 86400 + 10800 -- make sure it's the previous day + local previous_day_date = os.date("*t", previous_day_ts) + previous_day_ts = os.time({ + year = previous_day_date.year, + month = previous_day_date.month, + day = previous_day_date.day, + hour = 0, + min = 0, + }) + local previous_day_length = current_day_ts - previous_day_ts + if self.day_ts - previous_day_length >= self.min_ts then + -- go to previous day + self.day_ts = self.day_ts - previous_day_length + self:setupView() + end end return true end @@ -868,7 +900,10 @@ function CalendarDayView:refreshTimeline() CenterContainer:new{ dimen = Geom:new{ w = self.time_text_width, h = self.hour_height}, TextWidget:new{ - text = string.format("%02d:00", i), + text = string.format("%02d:%02d", + (i + (self.reader_statistics.settings.calendar_day_start_hour or 0)) % 24, + self.reader_statistics.settings.calendar_day_start_minute or 0 + ), face = self.time_text_face, padding = Size.padding.small } @@ -1358,7 +1393,8 @@ function CalendarView:_populateItems() show_parent = self, callback = not is_future and function() UIManager:show(CalendarDayView:new{ - day_ts = day_ts, + day_ts = day_ts + (self.reader_statistics.settings.calendar_day_start_hour or 0) * 3600 + + (self.reader_statistics.settings.calendar_day_start_minute or 0) * 60, reader_statistics = self.reader_statistics, title_callback = function(this) local day = os.date("%Y-%m-%d", this.day_ts + 10800) -- use 3:00 to determine date (summer time change) @@ -1397,7 +1433,7 @@ end function CalendarView:showCalendarDayView(reader_statistics, title_callback) local date = os.date("*t", os.time()) UIManager:show(CalendarDayView:new{ - day_ts = os.time({ year = date.year, month = date.month, day = date.day, hour = 0 }), + day_ts = os.time({ year = date.year, month = date.month, day = date.day, hour = reader_statistics.settings.calendar_day_start_hour or 0, min = reader_statistics.settings.calendar_day_start_minute or 0 }), reader_statistics = reader_statistics, title_callback = title_callback, min_month = self.min_month diff --git a/plugins/statistics.koplugin/main.lua b/plugins/statistics.koplugin/main.lua index 20d2f5a81..eebbeb701 100644 --- a/plugins/statistics.koplugin/main.lua +++ b/plugins/statistics.koplugin/main.lua @@ -1074,6 +1074,46 @@ The max value ensures a page you stay on for a long time (because you fell aslee end, separator = true, }, + { + text_func = function() + -- @translators %1 is the time in the format 00:00 + return T(_("Daily timeline starts at %1"), + string.format("%02d:%02d", self.settings.calendar_day_start_hour or 0, + self.settings.calendar_day_start_minute or 0) + ) + end, + callback = function(touchmenu_instance) + local DateTimeWidget = require("ui/widget/datetimewidget") + local start_of_day_widget = DateTimeWidget:new{ + hour = self.settings.calendar_day_start_hour or 0, + min = self.settings.calendar_day_start_minute or 0, + hour_max = 6, + ok_text = _("Set time"), + title_text = _("Daily timeline starts at"), + info_text =_([[ +Set the time when the daily timeline should start. + +If you read past midnight, and would like this reading session to be displayed on the same screen with your previous evening reading sessions, use a value such as 04:00. + +Time is in hours and minutes.]]), + callback = function(time) + self.settings.calendar_day_start_hour = time.hour + self.settings.calendar_day_start_minute = time.min + touchmenu_instance:updateItems() + end + } + UIManager:show(start_of_day_widget) + end, + keep_menu_open = true, + }, + { + text = _("Also use in calendar view"), + checked_func = function() return self.settings.calendar_use_day_time_shift end, + callback = function() + self.settings.calendar_use_day_time_shift = not self.settings.calendar_use_day_time_shift + end, + separator = true, + }, { text = _("Cloud sync"), callback = function(touchmenu_instance) @@ -2004,6 +2044,7 @@ function ReaderStatistics:getBooksFromPeriod(period_begin, period_end, callback_ table.insert(results, { result_book[1][i], T(N_("%1 (1 page)", "%1 (%2 pages)", tonumber(result_book[2][i])), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false), tonumber(result_book[2][i])), + duration = tonumber(result_book[3][i]), book_id = tonumber(result_book[4][i]), callback = function() local kv = self.kv @@ -2699,14 +2740,20 @@ function ReaderStatistics:getReadingRatioPerHourByDay(month) -- We let SQLite compute these timestamp boundaries from the provided -- month; we need the start of the month to be a real date: month = month.."-01" + local offset = not self.settings.calendar_use_day_time_shift and 0 or (self.settings.calendar_day_start_hour or 0) * 3600 + (self.settings.calendar_day_start_minute or 0) * 60 local sql_stmt = [[ SELECT strftime('%Y-%m-%d', start_time, 'unixepoch', 'localtime') day, strftime('%H', start_time, 'unixepoch', 'localtime') hour, sum(duration)/3600.0 ratio - FROM page_stat - WHERE start_time BETWEEN strftime('%s', ?, 'utc') - AND strftime('%s', ?, 'utc', '+33 days', 'start of month', '-1 second') + FROM ( + SELECT + start_time-? as start_time, + duration + FROM page_stat + WHERE start_time BETWEEN strftime('%s', ?, 'utc') + AND strftime('%s', ?, 'utc', '+33 days', 'start of month', '-1 second') + ) GROUP BY strftime('%Y-%m-%d', start_time, 'unixepoch', 'localtime'), strftime('%H', start_time, 'unixepoch', 'localtime') @@ -2714,7 +2761,7 @@ function ReaderStatistics:getReadingRatioPerHourByDay(month) ]] local conn = SQ3.open(db_location) local stmt = conn:prepare(sql_stmt) - local res, nb = stmt:reset():bind(month, month):resultset("i") + local res, nb = stmt:reset():bind(offset, month, month):resultset("i") stmt:close() conn:close() local per_day = {} @@ -2731,16 +2778,20 @@ end function ReaderStatistics:getReadBookByDay(month) month = month.."-01" + local offset = not self.settings.calendar_use_day_time_shift and 0 or (self.settings.calendar_day_start_hour or 0) * 3600 + (self.settings.calendar_day_start_minute or 0) * 60 local sql_stmt = [[ SELECT strftime('%Y-%m-%d', start_time, 'unixepoch', 'localtime') day, sum(duration) durations, id_book book_id, - book.title book_title - FROM page_stat - JOIN book ON book.id = page_stat.id_book - WHERE start_time BETWEEN strftime('%s', ?, 'utc') - AND strftime('%s', ?, 'utc', '+33 days', 'start of month', '-1 second') + title book_title + FROM ( + SELECT start_time-? as start_time, duration, page_stat.id_book, book.title + FROM page_stat + JOIN book ON book.id = page_stat.id_book + WHERE start_time BETWEEN strftime('%s', ?, 'utc') + AND strftime('%s', ?, 'utc', '+33 days', 'start of month', '-1 second') + ) GROUP BY strftime('%Y-%m-%d', start_time, 'unixepoch', 'localtime'), id_book, @@ -2749,7 +2800,7 @@ function ReaderStatistics:getReadBookByDay(month) ]] local conn = SQ3.open(db_location) local stmt = conn:prepare(sql_stmt) - local res, nb = stmt:reset():bind(month, month):resultset("i") + local res, nb = stmt:reset():bind(offset, month, month):resultset("i") stmt:close() conn:close() local per_day = {}