Merge pull request 'Add an RSS feed' (#1) from lucidiot/books:rss into main

Reviewed-on: http://git.tilde.town/dozens/books/pulls/1
Dozens B. McCuzzins 2021-04-17 16:58:06 +00:00
commit 53d8718ee9
3 changed files with 101 additions and 0 deletions

View File

@ -18,3 +18,14 @@ It will allow you to do stuff like:
- find the largest pdfs in the collection: `csvcut -c title_sort,formats,size books.csv | csvgrep -c formats -m pdf | csvsort -c size -r | head`
- `csvjson books.csv | jq | whatever`
- You can also perform actual SQL queries on it, and convert the data between csv and sqlite database: <https://csvkit.readthedocs.io/en/latest/tutorial/3_power_tools.html>
## RSS feed
An RSS feed has been kindly provided by [the Rsszard of Syndication](https://tilde.town/~lucidiot)
and is available at https://AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.
Generating the feed requires you to have Python 3.7 or later installed, as well
as the [xmltodict](https://pypi.org/project/xmltodict) package:
`pip3 install xmltodict`.
To generate the feed, run `./geenfeed.py <books.csv >lefeed.xml`.

89
genfeed.py 100755
View File

@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from datetime import datetime, timezone
from typing import Mapping, MutableMapping
import csv
import sys
import xmltodict
RSS_DATE_FORMAT = '%a, %d %b %Y %T %z'
ISO_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S%z'
def parse_book(book: MutableMapping[str, str]) -> Mapping:
item = {
"title": book["title_sort"],
"pubDate": datetime.strptime(book.pop("timestamp"), ISO_DATE_FORMAT)
.strftime(RSS_DATE_FORMAT),
"guid": {
"@isPermaLink": "false",
"#text": book.pop("uuid"),
},
"description": book.pop("comments"),
# The CSV's first character is a non-breaking space for some reason,
# which breaks the author column
"author": book.get("author_sort") or book["\ufeffauthor_sort"],
}
# Prepend metadata to the item description
item["description"] = "<dl>{}</dl>{}".format(
"".join(
"<dt>{}</dt><dd>{}</dd>".format(
key.replace('_sort', '').replace('_', ' ').replace('\ufeff', '').capitalize(),
value,
)
for key, value in book.items()
# Ignore empty columns
if value
),
item['description']
)
if book.get("tags"):
item["category"] = [
{
"@domain": "https://git.tilde.town/dozens/books",
"#text": tag
}
for tag in book["tags"].split(", ")
]
return item
def main():
sys.stdout.write(xmltodict.unparse({
"rss": {
"@version": "2.0",
"@xmlns:atom": "http://www.w3.org/2005/Atom",
"@xmlns:sy": "http://purl.org/rss/1.0/modules/syndication/",
"channel": {
"title": "dozens books",
"description": "the cool calibre library of dozens",
"link": "https://git.tilde.town/dozens/books",
"atom:link": {
"@rel": "self",
"@type": "application/rss+xml",
# TODO: set the correct public URL of your feed!
"@href": "http://fuck",
},
"language": "en-US",
"pubDate": datetime.now(timezone.utc)
.strftime(RSS_DATE_FORMAT),
"docs": "https://www.rssboard.org/rss-specification",
"webMaster": "dozens@tilde.town (~dozens)",
"generator": "Python " + ".".join(map(str, sys.version_info[:3])),
# Update on the first of every month, at midnight UTC
"sy:updatePeriod": "monthly",
"sy:updateFrequency": "1",
"sy:updateBase": "1971-01-01T00:00+00:00",
# One month, roughly, for clients that do not support mod_syndication
"ttl": 60 * 24 * 30,
"item": list(map(parse_book, csv.DictReader(sys.stdin))),
}
}
}, pretty=True, short_empty_elements=True))
if __name__ == '__main__':
main()

1
requirements.txt 100644
View File

@ -0,0 +1 @@
xmltodict>=0.12