From 9b39147c87075b46c179e944e6aa628a5174abe1 Mon Sep 17 00:00:00 2001 From: ktyl Date: Fri, 17 Nov 2023 23:44:58 +0000 Subject: [PATCH] feat(books): start collection --- src/garden/book-collecting.md | 106 ++++++++++++++++++++++++++++++++++ src/garden/books.py | 87 ++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 src/garden/book-collecting.md create mode 100644 src/garden/books.py diff --git a/src/garden/book-collecting.md b/src/garden/book-collecting.md new file mode 100644 index 0000000..bd24889 --- /dev/null +++ b/src/garden/book-collecting.md @@ -0,0 +1,106 @@ +how do you define a book collection? +my book collection is the set of all books. + +i prefer physical books to e-readers. +unfortunately i have quite a few these days. + +i want to read them all eventually! +i also tend to live in quite small places +and i want to be able to move city easily! + +so here's my system for organising my physical book collection. + +i want to: +* read books i already have +* read as many different books as possible +* minimise physical storage requirements +* keep track of books i've read +* gather books i don't already have + +constraints +* i don't know for sure what book i will want to read next + +for every book in the world +* i either have or have not read it +* i have access to it or i don't + +so i sort my book collection with 4 categories + +*-------------------*-----------------------* +| | | +| unread | read | +| have | have | +| | | +| 37ยบ2 le matin | L'Homme des Jeux | +| | | +*-------------------*-----------------------* +| | | +| unread | read | +| haven't | haven't | +| | | +| Das Kapital | Frankisstein | +| | | +*-------------------*-----------------------* + +i can then begin to optimise my collection. + +* i do not have this book, and i have read it. +* i have this book, but i have not read it. +* i have this book, and i have read it. +* i do not have this book, but i have not read it. + +the books i am most interested in having nearby are unread ones, as i would like to read as many different books as possible. + +books i have already read i don't need nearby anymore. +i might pass it on, or store it somewhere with less of a premium on space. +i could also attempt to track where it is! + +based on my requirements and my categories, i create four lists for the books + +* ready +* all done +* read and gone +* hunted + +that looks like a decent start to the system, so i suppose now i'll start collecting! + +so i'll use markdown lists in the format + +``` +* [x] author - title # reading +* [ ] author - title # nearby +``` +when collecting music i use artist - year - name +however, publication year is an extra step that will slow data entry, so won't use this to start with - i have a lot of books + +i realised i had a gpt-4 sub and that it could look at pictures now so i gave it a go +i fed it some photos and some formatting preferences and i got out perfect markdown lists + +``` +- [ ] Doctorow, Cory - Walkaway +- [ ] Ferreira, Pedro G. - The Perfect Theory +- [ ] Hadfield, Chris - An Astronaut's Guide to Life on Earth +- [ ] Heinlein, Robert A. - Beyond This Horizon +``` + +books are lovely are great to look at, but the mishmash of fonts and presentation are a nightmare for indexing. +now we have some good and lovely metadata :) +this is an imperfect method, as the only way i can check it is still by combing through the physical books manually +but it does let me target my combing after identifying problems in the index +and in the meantime gives us a bunch of data to play with + +markdown lists also allow me to mark some items in a list +this is looks flexible, so i think in my 'ready' list i will mark which book(s) i am currently reading + +``` +- [ ] Doctorow, Cory - Walkaway +- [ ] Ferreira, Pedro G. - The Perfect Theory +- [ ] Hadfield, Chris - An Astronaut's Guide to Life on Earth +- [ ] Heinlein, Robert A. - Beyond This Horizon +``` + +the other lists i will leave unmarked for now, until i think of something to do with them. + +as for doing things with them, i wrote a [python script](#) which processes the data in the now-populated all-done and ready lists to yield some interesting (?) and fun (?) results? + +[example output](#) diff --git a/src/garden/books.py b/src/garden/books.py new file mode 100644 index 0000000..4c71e5b --- /dev/null +++ b/src/garden/books.py @@ -0,0 +1,87 @@ +import sys +import os +import re + +def print_usage(): + print(f"usage: python {sys.argv[0]} DIR") + print(f"") + print(f"\tDIR\tdirectory containing markdown lists in files.") + + +if len(sys.argv) != 2: + print_usage() + exit(1) + +base_path = os.path.abspath(sys.argv[1]) +ready_list_name = "ready.md" +done_list_name = "all-done.md" + +def get_path(list_name : str) -> str: + return os.path.join(base_path, list_name) + + +def get_matches(list_name : str) -> list[re.Match]: + # Matches a markdown list item + entry_pattern = re.compile(r"^[*-] \[([ *x])\] (.+) - (.+)") + + matches = [] + with open(get_path(list_name)) as f: + matches = [entry_pattern.match(l) for l in f.readlines()] + return [m for m in matches if m is not None] + + +class Book: + def __init__(self, match : re.Pattern): + self.mark = match.group(1) != " " + self.author = match.group(2) + self.title = match.group(3) + + def is_metadata_complete(self): + if not self.title or not self.author: + return False + + if self.title == "???" or self.author == "???": + return False + + return True + + @staticmethod + def get_list(list_name : str, filter_partial_metadata = True) -> []: + books = [Book(m) for m in get_matches(list_name)] + + if filter_partial_metadata: + books = [b for b in books if b.is_metadata_complete()] + + return books + + +def print_section(title : str, books : list[str]): + print(f"# {title} ({len(books)})\n") + + longest_title = max([len(b.title) for b in books]) + title_column_width = longest_title + 2 + + for book in books: + row = [book.title, book.author] + format_str = "- {: <" + str(title_column_width) + "} {: <20}" + print(format_str.format(*row)) + print() + +def print_in_progress(): + books = [b for b in Book.get_list(ready_list_name, False) if b.mark] + print_section("in progress", books) + +def print_completed(): + books = Book.get_list(done_list_name) + print_section("up for borrowing", books) + +def print_partial_metadata(): + books = Book.get_list(ready_list_name, False) + books += Book.get_list(done_list_name, False) + books = [b for b in books if not b.is_metadata_complete()] + + print_section("metadata incomplete", books) + +print_in_progress() +print_completed() +print_partial_metadata()