From ae9d04ffa7e689d92a7a99341a1b235b96b87dbe Mon Sep 17 00:00:00 2001 From: Rose Liverman Date: Fri, 31 Jul 2026 20:50:50 -0600 Subject: [PATCH] mostly implemented an import system --- ReadMe.md | 19 +++---- asymptome.lua | 144 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 10 deletions(-) mode change 100644 => 100755 asymptome.lua diff --git a/ReadMe.md b/ReadMe.md index abce0ee..1bc2d0d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -3,18 +3,14 @@ CLI tracker to prioritize your limited time by ELO ranking media. ## Initial Ideas - [ ] 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. - [ ] 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 pages as an option, - [ ] 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. - -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) +- [x] Store items as an array instead of object, because that makes the JSON output more consistent -> smaller JSON commit differences. ## Contributions @@ -30,6 +26,7 @@ Licensing? I don't care. Do whatever you want. IP rights are stupid. "title": "Title", "author": "Author", "series": "Series", + "genre": "Fiction", "pages": 100, "priority": 0, "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. -- At least one of `title`, `author`, or `series` must be defined. +- `title`, `author`, `series`, `pages`, `genre`, and `hidden` are optional. +- At least one of `title`, `author`, `series`, or `genre` must be defined. - `priority` is the ELO ranking, initializing to `0`. - `progress` represents read/unread/in-progress status. `0` is unread, `1` is read, and anything in-between in a percentage of progress. - `hidden` is undefined or `true` to hide a book from ALL of the interface 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" ? A combination of asymptote - because you will die before you finish your reading list, always getting closer but never arriving - and tome. diff --git a/asymptome.lua b/asymptome.lua old mode 100644 new mode 100755 index 409c32c..dfc56ec --- a/asymptome.lua +++ b/asymptome.lua @@ -66,6 +66,13 @@ local filters = { end return false 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 @@ -84,7 +91,7 @@ sort_orders = { end, most_pages_remaining = function(A, B) 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 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 @@ -96,6 +103,47 @@ sort_orders = { 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 if path_exists(default_file) then 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 ] to add a book. [i ] to import from a JSON file.") +-- TODO define what can go in 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