{"id":14531025,"url":"https://github.com/fpsvogel/reading","last_synced_at":"2025-05-04T15:33:15.253Z","repository":{"id":56891257,"uuid":"382413109","full_name":"fpsvogel/reading","owner":"fpsvogel","description":"A Ruby gem that parses your CSV reading log.","archived":false,"fork":false,"pushed_at":"2024-10-13T02:39:30.000Z","size":1182,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-26T15:53:12.324Z","etag":null,"topics":["book-tracker","reading","reading-log","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fpsvogel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-02T17:08:22.000Z","updated_at":"2024-10-13T02:39:34.000Z","dependencies_parsed_at":"2024-05-16T03:01:49.664Z","dependency_job_id":"4685db89-fb2e-4d41-b2f3-4f3901260aa3","html_url":"https://github.com/fpsvogel/reading","commit_stats":{"total_commits":160,"total_committers":1,"mean_commits":160.0,"dds":0.0,"last_synced_commit":"985a028f1f267b41fdce54b2271f72e2c464363f"},"previous_names":["fpsvogel/reading-csv"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpsvogel%2Freading","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpsvogel%2Freading/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpsvogel%2Freading/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpsvogel%2Freading/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fpsvogel","download_url":"https://codeload.github.com/fpsvogel/reading/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252356467,"owners_count":21734952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["book-tracker","reading","reading-log","ruby"],"created_at":"2024-09-05T00:01:11.492Z","updated_at":"2025-05-04T15:33:14.893Z","avatar_url":"https://github.com/fpsvogel.png","language":"Ruby","readme":"\u003ch1 align=\"center\"\u003eReading\u003c/h1\u003e\n\nReading is a Ruby gem that parses a CSV reading log. My personal site's [Reading List](https://fpsvogel.com/reading/) and [Reading Statistics](https://fpsvogel.com/reading-stats/) pages are built with the help of this gem.\n\n### Table of Contents\n\n- [Why?](#why)\n- [Installation](#installation)\n- [Docs](#docs)\n- [Usage](#usage)\n  - [Try out a CSV string](#try-out-a-csv-string)\n  - [Parse in Ruby](#parse-in-ruby)\n  - [Parse with custom config](#parse-with-custom-config)\n  - [Filtering the output](#filtering-the-output)\n  - [Get statistics on your reading](#get-statistics-on-your-reading)\n- [How to add a reading page to your site](#how-to-add-a-reading-page-to-your-site)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Why?\n\nBecause I love reading, and keeping a plain-text reading log helps me remember, reflect on, and plan my reading (and listening, and watching).\n\nMy CSV reading log serves the same role as Goodreads used to, but it lets me do a few things that Goodreads doesn't:\n\n- Own my data.\n- Add items of any format: podcasts, documentaries, etc.\n- Edit and search in a plain text file, which I prefer over navigating a site or app. I can even pull up my reading log on my phone via a Dropbox-syncing text editor app—[Simple Text](https://play.google.com/store/apps/details?id=simple.text.dropbox) is the one I use.\n\nThis gem solves the biggest problem I had with a plain-text reading log: **how to share my favorite reads with friends?** The gem's parser transforms my `reading.csv` into data that I can selectively display on [my Reading List page](https://fpsvogel.com/reading/).\n\nThe Reading gem also gives statistics data, exemplified on [my Reading Statistics page](https://fpsvogel.com/reading-stats/).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem \"reading\"\n```\n\nAnd then execute:\n\n```\n$ bundle install\n```\n\nOr install it yourself as:\n\n```\n$ gem install reading\n```\n\n## Docs\n\n[CSV Format Guide](https://github.com/fpsvogel/reading/blob/main/doc/csv-format.md) on how to set up your own CSV reading log.\n\n[Parsed Output Guide](https://github.com/fpsvogel/reading/blob/main/doc/parsed-output.md) on the structure into which the Reading gem parses CSV rows.\n\n[`test/parse_test.rb`](https://github.com/fpsvogel/reading/blob/main/test/parse_test.rb) has more examples of the CSV format.\n\n## Usage\n\nFor examples of real-life usage, see [the LoadReadingList plugin](https://github.com/fpsvogel/fpsvogel.com/blob/main/plugins/builders/load_reading_list.rb) on my website, which gets my `reading.csv` from Dropbox and parses it. The parsed items are used on two pages:\n\n- [fpsvogel.com/reading](https://fpsvogel.com/reading/): [source](https://github.com/fpsvogel/fpsvogel.com/blob/main/src/reading_list.md) plus [view component source](https://github.com/fpsvogel/fpsvogel.com/tree/main/src/_components).\n- [fpsvogel.com/reading-stats](https://fpsvogel.com/reading-stats/): [source](https://github.com/fpsvogel/fpsvogel.com/blob/main/src/reading_stats.md).\n\n### Try out a CSV string\n\nTo quickly see the parsed output from a CSV string, use the `reading` command:\n\n```\n$ reading '3|📕Trying|Little Library 1970147288'\n```\n\nSee the [CSV Format Guide](https://github.com/fpsvogel/reading/blob/main/doc/csv-format.md) for more on columns, but here suffice it to note that this CSV string has the first three columns (Rating, Head, and Sources).\n\nAn optional second argument specifies enabled columns. The CSV string above already omits several right-side columns, but to omit a left-side or middle column we'll have to disable it. For example, to omit the Rating column from the example above:\n\n```\n$ reading '📕Trying|Little Library 1970147288' 'head, sources'\n```\n\nThe `reading` command can also start a prompt to query for reading statistics. For more on that, see [\"Get statistics on your reading\"](#get-statistics-on-your-reading) below.\n\n### Parse in Ruby\n\nTo parse a CSV reading log in Ruby rather than on the command line:\n\n```ruby\nrequire \"reading\"\n\nfile_path = \"/home/user/reading.csv\"\nitems = Reading.parse(path: file_path)\n```\n\nThis returns an array of [Items](https://github.com/fpsvogel/reading/blob/main/lib/reading/item.rb), which are essentially a wrapper with the same structure as the template Hash in `Config#default_config[:item][:template]` in [config.rb](https://github.com/fpsvogel/reading/blob/main/lib/reading/config.rb), but providing a few conveniences such as dot access (`item.notes` instead of `item[:notes]`).\n\nIf instead of a file path you want to directly parse a String (or anything else responding to `#each_line`, such as a `File`):\n\n```ruby\nrequire \"reading\"\n\ncsv_string = '3|📕Trying|Little Library 1970147288'\nitems = Reading.parse(lines: csv_string)\n```\n\n### Parse with custom config\n\nTo use custom configuration, pass a config Hash when initializing.\n\nHere's an example. If you don't want to use all the columns (as in [the minimal example in the CSV format guide](https://github.com/fpsvogel/reading/blob/main/doc/csv-format.md#a-minimal-reading-log)), you'll need to pass in a config including only the desired columns, like this:\n\n```ruby\nrequire \"reading\"\n\ncustom_config = { enabled_columns: [:head, :end_dates] }\nfile_path = \"/home/user/reading.csv\"\nitems = Reading.parse(path: file_path, config: custom_config)\n```\n\n### Filtering the output\n\nOnce you've parsed your reading log, you can easily filter the output like this:\n\n```ruby\n# ...\n# (Already parsed a reading log into `items` as above.)\nfiltered_items = Reading.filter(\n  items: items,\n  minimum_rating: 4,\n  status: [:done, :in_progress],\n  excluded_genres: [\"cats\", \"memoir\"],\n)\n```\n\n### Get statistics on your reading\n\nThe `reading` command can also start an interactive statistics-querying mode if the command is given the path to a CSV file:\n\n```\n$ reading /home/user/reading.csv\n```\n\nThen a prompt will appear, in which you can type commands made up of an **operation** optionally followed by one or more **groupings** and/or one or more **filters**.\n\nHere are a few examples:\n\n```\naverage rating\ntotal amount by month\ntotal items by year, genre\ntop 5 lengths status!=planned format=print, ebook\ntop 3 amounts by year rating\u003e1 done\u003c100% source~library\n```\n\n\u003e **Warning**\n\u003e The operation, grouping, and filter(s) *must* appear in that order, or else the query may yield unexpected results.\n\nYou can also get statistics via Ruby code rather than on the command line:\n\n```ruby\n# ...\n# (Already parsed a reading log into `items` as above.)\nresults = Reading.stats(\n  input: \"total amount by month\"\n  items: items,\n)\n```\n\n\u003c!-- omit in toc --\u003e\n#### Stats operations\n\nThe last word may be pluralized.\n\n- `average rating`\n- `average length`\n- `average amount`\n- `average daily-amount`\n- `list items` (or just `list`)\n- `total items` (or just `items`, or `count`)\n- `total amount` (or just `amount`)\n- `top/bottom [N] ratings`\n- `top/bottom [N] lengths`\n- `top/bottom [N] amounts`\n- `top/bottom [N] speeds`\n- `debug` to view the results in a Ruby debugger\n\n\u003c!-- omit in toc --\u003e\n#### Stats groupings\n\nThese too may be pluralized.\n\n- `by month`\n- `by year`\n- `by eachgenre` (single genres)\n- `by genre` (combinations of genres)\n- `by rating`\n- `by format`\n- `by source`\n- `by length`\n\n\u003c!-- omit in toc --\u003e\n#### Stats filters\n\nThese may be pluralized, and may be followed by any of the operators listed for each. The operators `~` and `!~` mean \"contains\" and \"does not contain\", respectively.\n\n- `genre` (`=`, `!=`)\n- `rating` (`=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `!=`)\n- `format` (`=`, `!=`)\n- `source` (`=`, `!=`, `~`, `!~`)\n- `title` (`=`, `!=`, `~`, `!~`)\n- `author` (`=`, `!=`, `~`, `!~`)\n- `series` (`=`, `!=`, `~`, `!~`)\n- `note` (`=`, `!=`, `~`, `!~`)\n- `status` (`=`, `!=`)\n- `length` (`=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `!=`)\n- `progress` (`=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `!=`)\n- `experiences` (i.e. number of reads) (`=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `!=`)\n- `date` (`=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `!=`)\n- `end-date` (`=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `!=`)\n\n## How to add a reading page to your site\n\nAfter Reading parses your CSV reading log, it's up to you to display that parsed information on a web page. I've set up my personal site so that it parses my reading log during site generation, and it's even automatically generated every week. That means my site's \"Reading\" page automatically syncs to my reading log on a weekly basis.\n\nI explain how I did this in my tutorial [\"Build a blog with Bridgetown\"](https://fpsvogel.com/posts/2021/build-a-blog-with-bridgetown), which may give you ideas even if you don't use [Bridgetown](https://www.bridgetownrb.com/) to build your site… but you should use Bridgetown, it's great 😉\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/fpsvogel/reading.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["Haskell"],"sub_categories":["Projects"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffpsvogel%2Freading","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffpsvogel%2Freading","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffpsvogel%2Freading/lists"}