Statistics: some fixes, reordering and rewording (#6194)

- Fix wrong values for books opened (or first opened)
  from April 14th to May 20th: highlights and notes
  possibly being NULL was preventing nb of pages and
  last_open from being fetched.
- Re-order current book and all books stat items to some
  hopefully more sensible order.
- Some rewording for clarity.

KeyValuePage:
- Have value_overflow_align="right" only align right
  when value overflows 1/2 screen width, but not
  when only key overflows that.
- Show truncated text also on Tap when there is no
  callback.
reviewable/pr6209/r1
poire-z 4 years ago committed by GitHub
parent dd0cdb9dcb
commit c65c33d75f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -124,20 +124,14 @@ local KeyValueItem = InputContainer:new{
textviewer_width = nil,
textviewer_height = nil,
value_overflow_align = "left",
-- "right": only align right if value overflow 1/2 width
-- "right_always": align value right even when small and
-- only key overflows 1/2 width
}
function KeyValueItem:init()
self.dimen = Geom:new{w = self.width, h = self.height}
if self.callback and Device:isTouchDevice() then
self.ges_events.Tap = {
GestureRange:new{
ges = "tap",
range = self.dimen,
}
}
end
-- self.value may contain some control characters (\n \t...) that would
-- be rendered as a square. Replace them with a shorter and nicer '|'.
-- (Let self.value untouched, as with Hold, the original value can be
@ -190,12 +184,13 @@ function KeyValueItem:init()
key_w = key_w_rendered
end
value_align_right = true -- so the ellipsis touches the screen right border
if self.value_overflow_align ~= "right" and self.value_align ~= "right" then
if self.value_align ~= "right" and self.value_overflow_align ~= "right"
and self.value_overflow_align ~= "right_always" then
-- Don't adjust the ellipsis to the screen right border,
-- so the left of text is aligned with other truncated texts
fit_right_align = false
end
-- Allow for displaying the non-truncated texts with Hold
-- Allow for displaying the non-truncated text with Hold
if Device:isTouchDevice() then
self.ges_events.Hold = {
GestureRange:new{
@ -203,10 +198,18 @@ function KeyValueItem:init()
range = self.dimen,
}
}
-- If no tap callback, allow for displaying the non-truncated
-- text with Tap too
if not self.callback then
self.callback = function()
self:onHold()
end
end
end
else
-- Both can fit: break the 1/2 widths
if self.value_overflow_align == "right" or self.value_align == "right" then
if self.value_align == "right" or self.value_overflow_align == "right_always"
or (self.value_overflow_align == "right" and value_w_rendered > value_w) then
key_w = available_width - value_w_rendered
value_align_right = true
else
@ -237,6 +240,15 @@ function KeyValueItem:init()
-- For debugging positioning:
-- value_widget = FrameContainer:new{ padding=0, margin=0, bordersize=1, value_widget }
if self.callback and Device:isTouchDevice() then
self.ges_events.Tap = {
GestureRange:new{
ges = "tap",
range = self.dimen,
}
}
end
self[1] = FrameContainer:new{
padding = frame_padding,
bordersize = 0,

@ -6,6 +6,7 @@ local Device = require("device")
local DocSettings = require("docsettings")
local InfoMessage = require("ui/widget/infomessage")
local KeyValuePage = require("ui/widget/keyvaluepage")
local Math = require("optmath")
local ReaderFooter = require("apps/reader/modules/readerfooter")
local ReaderProgress = require("readerprogress")
local ReadHistory = require("readhistory")
@ -171,7 +172,7 @@ function ReaderStatistics:init()
ReaderGesture.getBookStats = function()
if self:isDocless() or not self.is_enabled then return end
local stats = KeyValuePage:new{
title = _("Statistics"),
title = _("Current statistics"),
kv_pairs = self:getCurrentStat(self.id_curr_book),
}
return stats
@ -834,7 +835,7 @@ The max value ensures a page you stay on for a long time (because you fell aslee
keep_menu_open = true,
callback = function()
UIManager:show(KeyValuePage:new{
title = _("Statistics"),
title = _("Current statistics"),
kv_pairs = self:getCurrentStat(self.id_curr_book),
})
end,
@ -902,15 +903,14 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
"----",
{ _("Last week"),"",
{ _("Books by week"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Last week"),
title = _("Books by week"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(7, "daily_weekday"),
kv_pairs = self:getDatesFromAll(0, "weekly", true),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -919,14 +919,14 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
{ _("Last month by day"),"",
{ _("Books by month"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Last month by day"),
title = _("Books by month"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(30, "daily_weekday"),
kv_pairs = self:getDatesFromAll(0, "monthly", true),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -935,14 +935,15 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
{ _("Last year by day"),"",
"----",
{ _("Last week"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Last year by day"),
title = _("Last week"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(365, "daily"),
kv_pairs = self:getDatesFromAll(7, "daily_weekday"),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -951,14 +952,14 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
{ _("Last year by week"),"",
{ _("Last month by day"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Last year by week"),
title = _("Last month by day"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(365, "weekly"),
kv_pairs = self:getDatesFromAll(30, "daily_weekday"),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -967,14 +968,14 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
{ _("All stats by month"),"",
{ _("Last year by day"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("All stats by month"),
title = _("Last year by day"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(0, "monthly"),
kv_pairs = self:getDatesFromAll(365, "daily"),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -983,15 +984,14 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
"----",
{ _("Books by week"),"",
{ _("Last year by week"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Books by week"),
title = _("Last year by week"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(0, "weekly", true),
kv_pairs = self:getDatesFromAll(365, "weekly"),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -1000,14 +1000,14 @@ function ReaderStatistics:statMenu()
UIManager:show(self.kv)
end,
},
{ _("Books by month"),"",
{ _("All stats by month"),"",
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Books by month"),
title = _("All stats by month"),
value_overflow_align = "right",
kv_pairs = self:getDatesFromAll(0, "monthly", true),
kv_pairs = self:getDatesFromAll(0, "monthly"),
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -1015,7 +1015,7 @@ function ReaderStatistics:statMenu()
}
UIManager:show(self.kv)
end,
}
},
}
}
UIManager:show(self.kv)
@ -1083,7 +1083,7 @@ function ReaderStatistics:getCurrentStat(id_book)
local current_period, current_pages = self:getCurrentBookStats()
local conn = SQ3.open(db_location)
local notes, highlights = conn:rowexec(string.format("SELECT notes, highlights FROM book WHERE id = '%s';)", id_book))
local highlights, notes = conn:rowexec(string.format("SELECT highlights, notes FROM book WHERE id = '%s';)", id_book)) -- luacheck: no unused
local sql_stmt = [[
SELECT count(*)
FROM (
@ -1094,13 +1094,15 @@ function ReaderStatistics:getCurrentStat(id_book)
)
]]
local total_days = conn:rowexec(string.format(sql_stmt, id_book))
sql_stmt = [[
SELECT sum(period),
count(DISTINCT page)
count(DISTINCT page),
min(start_time)
FROM page_stat
WHERE id_book = '%s'
]]
local total_time_book, total_read_pages = conn:rowexec(string.format(sql_stmt, id_book))
local total_time_book, total_read_pages, first_open = conn:rowexec(string.format(sql_stmt, id_book))
conn:close()
if total_time_book == nil then
@ -1109,6 +1111,9 @@ function ReaderStatistics:getCurrentStat(id_book)
if total_read_pages == nil then
total_read_pages = 0
end
if first_open == nil then
first_open = TimeVal:now().sec
end
self.data.pages = self.view.document:getPageCount()
total_time_book = tonumber(total_time_book)
total_read_pages = tonumber(total_read_pages)
@ -1117,22 +1122,32 @@ function ReaderStatistics:getCurrentStat(id_book)
local estimate_end_of_read_date = os.date("%Y-%m-%d", tonumber(os.time() + estimate_days_to_read * 86400))
local formatstr = "%.0f%%"
return {
-- Global statistics (may consider other books than current book)
-- since last resume
{ _("Time spent reading this session"), util.secondsToClock(current_period, false) },
{ _("Pages read this session"), tonumber(current_pages) },
-- today
{ _("Time spent reading today"), util.secondsToClock(today_period, false) },
{ _("Pages read today"), tonumber(today_pages) },
{ _("Time to read"), util.secondsToClock(time_to_read), false},
{ _("Total time"), util.secondsToClock(total_time_book, false) },
{ _("Total highlights"), tonumber(highlights) },
{ _("Total notes"), tonumber(notes) },
{ _("Total pages read"), tonumber(total_read_pages) },
{ _("Total days"), tonumber(total_days) },
"----",
-- Current book statistics
{ _("Time spent reading this book"), util.secondsToClock(total_time_book, false) },
-- per days
{ _("Reading started"), os.date("%Y-%m-%d (%H:%M)", tonumber(first_open))},
{ _("Days reading this book"), tonumber(total_days) },
{ _("Average time per day"), util.secondsToClock(total_time_book/tonumber(total_days), false) },
-- per page
{ _("Pages read"), tonumber(total_read_pages) },
{ _("Average time per page"), util.secondsToClock(self.avg_time, false) },
{ _("Current pages read/Total pages"), self.curr_page .. "/" .. self.data.pages },
-- estimation, from current page to end of book
{ _("Current page/Total pages"), self.curr_page .. "/" .. self.data.pages },
{ _("Percentage completed"), formatstr:format(self.curr_page/self.data.pages * 100) },
{ _("Average time per day"), util.secondsToClock(total_time_book/tonumber(total_days)), false },
{ _("Estimated time to read"), util.secondsToClock(time_to_read, false) },
{ _("Estimated reading finished"),
T(N_("%1 (1 day)", "%1 (%2 days)", estimate_days_to_read), estimate_end_of_read_date, estimate_days_to_read) },
{ _("Highlights"), tonumber(highlights) },
-- { _("Total notes"), tonumber(notes) }, -- not accurate, don't show it
}
end
@ -1142,11 +1157,21 @@ function ReaderStatistics:getBookStat(id_book)
end
local conn = SQ3.open(db_location)
local sql_stmt = [[
SELECT title, authors, notes, highlights, pages, last_open
SELECT title, authors, pages, last_open, highlights, notes
FROM book
WHERE id = '%s'
]]
local title, authors, notes, highlights, pages, last_open = conn:rowexec(string.format(sql_stmt, id_book))
local title, authors, pages, last_open, highlights, notes = conn:rowexec(string.format(sql_stmt, id_book))
-- Due to some bug, some books opened around April 2020 might
-- have notes and highlight NULL in the DB.
-- See: https://github.com/koreader/koreader/issues/6190#issuecomment-633693940
-- (We made these last in the SQL so NULL/nil doesn't prevent
-- fetching the other fields.)
-- Show "?" when these values are not known (they will be
-- fixed next time this book is opened).
highlights = highlights and tonumber(highlights) or "?"
notes = notes and tonumber(notes) or "?" -- luacheck: no unused
sql_stmt = [[
SELECT count(*)
@ -1161,18 +1186,13 @@ function ReaderStatistics:getBookStat(id_book)
sql_stmt = [[
SELECT sum(period),
count(DISTINCT page)
count(DISTINCT page),
min(start_time)
FROM page_stat
WHERE id_book = '%s'
]]
local total_time_book, total_read_pages = conn:rowexec(string.format(sql_stmt, id_book))
local total_time_book, total_read_pages, first_open = conn:rowexec(string.format(sql_stmt, id_book))
sql_stmt = [[
SELECT min(start_time)
FROM page_stat
WHERE id_book = '%s'
]]
local first_open = conn:rowexec(string.format(sql_stmt, id_book))
conn:close()
if total_time_book == nil then
@ -1181,6 +1201,9 @@ function ReaderStatistics:getBookStat(id_book)
if total_read_pages == nil then
total_read_pages = 0
end
if first_open == nil then
first_open = TimeVal:now().sec
end
total_time_book = tonumber(total_time_book)
total_read_pages = tonumber(total_read_pages)
pages = tonumber(pages)
@ -1191,23 +1214,24 @@ function ReaderStatistics:getBookStat(id_book)
return {
{ _("Title"), title},
{ _("Authors"), authors},
{ _("First opened"), os.date("%Y-%m-%d (%H:%M)", tonumber(first_open))},
{ _("Last opened"), os.date("%Y-%m-%d (%H:%M)", tonumber(last_open))},
{ _("Total time"), util.secondsToClock(total_time_book, false) },
{ _("Total highlights"), tonumber(highlights) },
{ _("Total notes"), tonumber(notes) },
{ _("Total days"), tonumber(total_days) },
{ _("Reading started"), os.date("%Y-%m-%d (%H:%M)", tonumber(first_open))},
{ _("Last read"), os.date("%Y-%m-%d (%H:%M)", tonumber(last_open))},
{ _("Days reading this book"), tonumber(total_days) },
{ _("Time spent reading this book"), util.secondsToClock(total_time_book, false) },
{ _("Average time per day"), util.secondsToClock(total_time_book/tonumber(total_days), false) },
{ _("Average time per page"), util.secondsToClock(avg_time_per_page, false) },
-- These 2 ones are about page actually read (not the current page and % into book)
{ _("Read pages/Total pages"), total_read_pages .. "/" .. pages },
-- adding 0.5 rounds to nearest integer with math.floor
{ _("Percentage completed"), math.floor(total_read_pages / pages * 100 + 0.5) .. "%" },
{ _("Percentage read"), Math.round(total_read_pages / pages * 100) .. "%" },
{ _("Highlights"), highlights },
-- { _("Total notes"), notes }, -- not accurate, don't show it
"----",
{ _("Show days"), _("Tap to display"),
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Read in days"),
title = T(_("Days reading %1"), title),
value_overflow_align = "right",
kv_pairs = self:getDatesForBook(id_book),
callback_return = function()
@ -1286,7 +1310,7 @@ function ReaderStatistics:callbackMonthly(begin, finish, date_text, book_mode)
UIManager:close(kv)
if book_mode then
self.kv = KeyValuePage:new{
title = T(_("Books from: %1"), date_text),
title = T(_("Books read in %1"), date_text),
value_align = "right",
kv_pairs = self:getBooksFromPeriod(begin, finish),
callback_return = function()
@ -1313,7 +1337,7 @@ function ReaderStatistics:callbackWeekly(begin, finish, date_text, book_mode)
UIManager:close(kv)
if book_mode then
self.kv = KeyValuePage:new{
title = T(_("Books from: %1"), date_text),
title = T(_("Books read in %1"), date_text),
value_align = "right",
kv_pairs = self:getBooksFromPeriod(begin, finish),
callback_return = function()
@ -1482,7 +1506,7 @@ function ReaderStatistics:getDaysFromPeriod(period_begin, period_end)
local kv = self.kv
UIManager:close(kv)
self.kv = KeyValuePage:new{
title = T(_("Books in %1"), result_book[1][i]),
title = T(_("Books read %1"), result_book[1][i]),
value_overflow_align = "right",
kv_pairs = self:getBooksFromPeriod(time_begin, time_begin + 86400),
callback_return = function()
@ -1497,7 +1521,7 @@ function ReaderStatistics:getDaysFromPeriod(period_begin, period_end)
return results
end
function ReaderStatistics:getBooksFromPeriod(period_begin, period_end)
function ReaderStatistics:getBooksFromPeriod(period_begin, period_end, callback_shows_days)
local results = {}
local sql_stmt_res_book = [[
SELECT book_tbl.title AS title,
@ -1522,15 +1546,27 @@ function ReaderStatistics:getBooksFromPeriod(period_begin, period_end)
callback = function()
local kv = self.kv
UIManager:close(self.kv)
self.kv = KeyValuePage:new{
title = _("Read in days"),
value_overflow_align = "right",
kv_pairs = self:getDatesForBook(tonumber(result_book[4][i])),
callback_return = function()
UIManager:show(kv)
self.kv = kv
end
}
if callback_shows_days then -- not used currently by any code
self.kv = KeyValuePage:new{
title = T(_("Days reading %1"), result_book[1][i]),
kv_pairs = self:getDatesForBook(tonumber(result_book[4][i])),
value_overflow_align = "right",
callback_return = function()
UIManager:show(kv)
self.kv = kv
end
}
else
self.kv = KeyValuePage:new{
title = result_book[1][i],
kv_pairs = self:getBookStat(tonumber(result_book[4][i])),
value_overflow_align = "right",
callback_return = function()
UIManager:show(kv)
self.kv = kv
end
}
end
UIManager:show(self.kv)
end,
})
@ -1659,8 +1695,8 @@ function ReaderStatistics:getTotalStats()
self.kv = KeyValuePage:new{
title = book_title,
value_overflow_align = "right",
kv_pairs = self:getBookStat(id_book),
value_overflow_align = "right",
callback_return = function()
UIManager:show(kv)
self.kv = kv
@ -1671,7 +1707,7 @@ function ReaderStatistics:getTotalStats()
})
end
conn:close()
return T(_("Total hours read %1"), util.secondsToClock(total_books_time, false)), total_stats
return T(_("Total time spent reading: %1"), util.secondsToClock(total_books_time, false)), total_stats
end
function ReaderStatistics:genResetBookSubItemTable()

Loading…
Cancel
Save