mostly implemented an import system

This commit is contained in:
2026-07-31 20:51:08 -06:00
parent f4efb0f48a
commit ae9d04ffa7
2 changed files with 153 additions and 10 deletions
+10 -9
View File
@@ -3,18 +3,14 @@ CLI tracker to prioritize your limited time by ELO ranking media.
## Initial Ideas ## Initial Ideas
- [ ] Mark books read/unread/in-progress. - [ ] Mark books read/unread/in-progress.
- [ ] Show top 10 / bottom 10. Show only in-progress or unread. - [x] Show top 10 / bottom 10. Show only in-progress or unread.
- [ ] Rank only in-progress or unread. - [ ] Rank only in-progress or unread.
- [ ] Track books by title, author, and series. Default is to recognize "by" or "(series)" and automatically sort into relevant fields. - [ ] Track books by title, author, and series. Default is to recognize "by" or "(series)" and automatically sort into relevant fields.
- [ ] But also almost nothing is actually *needed*. - [x] But also almost nothing is actually *needed*.
- [x] Store progress as a numerical value? (Unread = 0, read = 1, in-progress in-between? Too easy to forget how it works?) - [x] Store progress as a numerical value? (Unread = 0, read = 1, in-progress in-between? Too easy to forget how it works?)
- [x] Store pages as an option, - [x] Store pages as an option,
- [ ] allow a weighted priority based on remaining percentage or whatever. - [ ] allow a weighted priority based on remaining percentage or whatever.
- [ ] Store items as an array instead of object, because that makes the JSON output more consistent -> smaller JSON commit differences. - [x] Store items as an array instead of object, because that makes the JSON output more consistent -> smaller JSON commit differences.
I think the opening UI should be to display the top 10 books in-progress (sort order customizable by user), where pressing a number key
allows the user to immediately enter an updated progress. Additional alphabetic keys are an option to access other features. This way
tracker use is near frictionless. (0 maps to 10)
## Contributions ## Contributions
@@ -30,6 +26,7 @@ Licensing? I don't care. Do whatever you want. IP rights are stupid.
"title": "Title", "title": "Title",
"author": "Author", "author": "Author",
"series": "Series", "series": "Series",
"genre": "Fiction",
"pages": 100, "pages": 100,
"priority": 0, "priority": 0,
"progress": 0.5, "progress": 0.5,
@@ -37,14 +34,18 @@ Licensing? I don't care. Do whatever you want. IP rights are stupid.
} }
``` ```
- `title`, `author`, `series`, `pages`, and `hidden` are optional. - `title`, `author`, `series`, `pages`, `genre`, and `hidden` are optional.
- At least one of `title`, `author`, or `series` must be defined. - At least one of `title`, `author`, `series`, or `genre` must be defined.
- `priority` is the ELO ranking, initializing to `0`. - `priority` is the ELO ranking, initializing to `0`.
- `progress` represents read/unread/in-progress status. `0` is unread, `1` is - `progress` represents read/unread/in-progress status. `0` is unread, `1` is
read, and anything in-between in a percentage of progress. read, and anything in-between in a percentage of progress.
- `hidden` is undefined or `true` to hide a book from ALL of the interface - `hidden` is undefined or `true` to hide a book from ALL of the interface
without deleting its data. without deleting its data.
Despite the script supporting otherwise, I highly recommend only using this for
*specific book titles*. In my experience, using entries representing multiple
books makes it more difficult to prioritize and use.
## Why "Asymptome" ? ## Why "Asymptome" ?
A combination of asymptote - because you will die before you finish your reading A combination of asymptote - because you will die before you finish your reading
list, always getting closer but never arriving - and tome. list, always getting closer but never arriving - and tome.
Regular → Executable
+143 -1
View File
@@ -66,6 +66,13 @@ local filters = {
end end
return false return false
end, end,
exclude_series = function(book)
if book.hidden then return false end
if book.series and not (book.title or book.author) then
return false
end
return true
end,
} }
local sort_orders local sort_orders
@@ -84,7 +91,7 @@ sort_orders = {
end, end,
most_pages_remaining = function(A, B) most_pages_remaining = function(A, B)
if not (A.pages and B.pages) then -- if pages is unknown for both, fallback to percentage if not (A.pages and B.pages) then -- if pages is unknown for both, fallback to percentage
return A.progress > B.progress return A.progress < B.progress
end end
local average_page_count = data.total_pages / #data.books local average_page_count = data.total_pages / #data.books
local a = (A.pages or average_page_count) - (A.pages or average_page_count) * A.progress local a = (A.pages or average_page_count) - (A.pages or average_page_count) * A.progress
@@ -96,6 +103,47 @@ sort_orders = {
end, end,
} }
local get_display_name = function(book)
local result = ""
if book.title then
result = book.title
if book.author then
result = result .. " by " .. book.author
end
elseif book.author then
result = book.author .. " (author)"
end
if book.series then
if #result > 0 then
result = result .. " (" .. book.series .. ")"
else
result = book.series .. " (series)"
end
end
if book.genre then
if #result > 0 then
result = result .. " (" .. book.genre .. ")"
else
result = book.genre .. " (genre)"
end
end
if book.pages then
result = result .. " ~" .. math.floor(book.progress * book.pages) .. "/" .. book.pages .. " pages read"
elseif book.progress > 0 and book.progress < 1 then
result = result .. " ~" .. math.floor(book.progress * 100) .. "% read"
end
return result
end
-- TODO this procedural code should be split into functions where feasible
local data local data
if path_exists(default_file) then if path_exists(default_file) then
data = load_json(default_file) data = load_json(default_file)
@@ -115,4 +163,98 @@ else
}, },
}, },
} }
save_json(default_file, data)
end
-- main menu
-- TODO should be a loop/function to return to
local selected_books = {}
for i = 1, #data.books do
local book = data.books[i]
if filters[data.defaults.launch.filter](book) then
selected_books[#selected_books + 1] = book
end
end
table.sort(selected_books, sort_orders[data.defaults.launch.sort])
for i = 1, math.min(10, #selected_books) do
local book = selected_books[i]
print(" " .. (i == 10 and "0" or i) .. ". " .. get_display_name(book))
end
print("Commands: " .. (#selected_books > 0 and "[0-9] to modify a book's progress. " or "") .. "[a <book>] to add a book. [i <file>] to import from a JSON file.")
-- TODO define what can go in <book> and how it will be interpreted
-- TODO actually implement modifying progress and adding books
-- TODO implement elo ranking
-- TODO type title to add or edit an extant book (this is better than the "a" command I wrote above)
local input = io.read("*line")
if input:sub(1, 1) == "i" then
-- TODO this should all be broken out into a function
import = load_json(input:sub(3))
if import.books then
for i = 1, #import.books do
data.books[#data.books + 1] = import.books[i]
-- TODO detect and ignore duplicates (would be better to provide user an option for what to do with duplicates)
-- default should accept highest progress of duplicates?
end
else -- assume we are importing from decider.lua's format
for title, value in pairs(import) do
local run = function()
local book = { -- minimum required book data strucutre
priority = 0,
progress = 0,
}
local i = title:find(" %(.-series.-%)")
if i then
book.series = title:sub(1, i - 1)
end
local i = title:find(" %(trilogy%)")
if i then
book.series = title:sub(1, i - 1)
end
local i = title:find(" %(.-author%)")
if i then
book.author = title:sub(1, i - 1)
end
local i = title:find(" %(genre%)")
if i then
print("Skipping import of \"" .. title .. "\"")
return false
end
if not (book.series or book.author) then
-- TODO detect " by " and split if needed
book.title = title
end
if type(value) == "number" then
book.priority = value
else
book.priority = value.score
if value.done == true then
book.progress = 1
elseif value.done == false then
book.hidden = true
end
end
-- TODO detect duplicates before adding them
data.books[#data.books + 1] = book
end
run()
end
end
save_json(default_file, data)
-- TODO there should be a sort of main menu I am returned to
end end