From 66b5366b7b377f9e595ee9b97261b65a0064aa5b Mon Sep 17 00:00:00 2001 From: Kévin Le Gouguec Date: Wed, 23 Feb 2022 10:26:02 +0100 Subject: Add script to update feeds --- admin/feeds/build-feed.py | 186 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100755 admin/feeds/build-feed.py (limited to 'admin/feeds/build-feed.py') diff --git a/admin/feeds/build-feed.py b/admin/feeds/build-feed.py new file mode 100755 index 0000000..5fe5c04 --- /dev/null +++ b/admin/feeds/build-feed.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 + +from datetime import datetime +import json +import re +from sys import argv +from urllib.parse import urljoin + +from lxml.builder import E +from lxml.etree import CDATA, XML, indent, tostring + +from helpers import ( + DATE_FORMATTERS, + guess_language, + read_concerts, + tmplocale, + touchup_plaintext, +) + + +# TODO: handle timezones correctly. +# Places to disambiguate: +# +# - concerts.in: +# either add the zone explicitly, or deduce it from the place, +# assuming all times in concerts.in are local times. +# +# - concerts-pubdates.json: +# just add the zone explicitly. +# +# Until then, assume all these "naive times" describe the same timezone +# (CET/CEST). + + +TIMEZONE = datetime.now().astimezone().tzinfo +NOW = datetime.now(tz=TIMEZONE) +DATE_FORMAT = '%-d %b %Y %H:%M %z' + +# TODO: add item pubDate + + +LOCALIZED_TEXT = { + 'en': { + 'title': 'Bellefeuille Quartet', + 'indexpath': 'en/', + 'description': 'News from the Bellefeuille quartet', + }, + 'fr': { + 'title': 'Quatuor Bellefeuille', + 'indexpath': '/', + 'description': 'Des nouvelles du quatuor Bellefeuille', + }, +} + +LOCALIZED_FORMATS = { + 'en': { + 'title': lambda c: f'{c.time.strftime("%B %-d %Y")} in {c.place}', + }, + 'fr': { + 'title': lambda c: f'{c.time.strftime("%-d %B %Y")} à {c.place}', + }, +} + + +def join(sequence, joiner_factory): + # There's got to be a standard itertools/functools thingy to do that… + result = [] + + for i, item in enumerate(sequence, start=1): + result.append(item) + + if i == len(sequence): + return result + + result.append(joiner_factory()) + + +CDATA_INDENT = 8*' ' + + +def cdata_concert(concert, lang): + formatters = DATE_FORMATTERS[lang] + + blocks = [] + + if concert.warning is not None: + blocks.append(E.p(concert.warning)) + + with tmplocale(lang): + blocks.extend(( + E.p(formatters['date'](concert.time)), + E.p(formatters['time'](concert.time)), + )) + + blocks.extend(( + E.p(*join(concert.address.splitlines(), E.br)), + E.ol( + *(XML(f'
  • {touchup_plaintext(p)}
  • ') + for p in concert.pieces.splitlines()) + ), + *(E.p(line) for line in concert.instructions.splitlines()), + )) + + for b in blocks: + indent(b) + + html_blocks = (tostring(b, encoding='utf-8').decode() for b in blocks) + + cdata = '\n' + '\n'.join(html_blocks) + '\n' + cdata = re.sub('^', CDATA_INDENT, cdata, flags=re.MULTILINE) + + return CDATA(cdata) + + +def generate_concert(concert, concerts_url, pubdates, lang): + formatters = LOCALIZED_FORMATS[lang] + + with tmplocale(lang): + title = formatters['title'](concert) + + anchor = f'concert-{concert.time.strftime("%F")}' + + item = E.item( + E.title(title), + E.link(f'{concerts_url}#{anchor}'), + E.description(cdata_concert(concert, lang)), + ) + + pubdate_str = pubdates[concert.time.isoformat(timespec='minutes')] + + if pubdate_str is not None: + pubdate = datetime.fromisoformat(pubdate_str).replace(tzinfo=TIMEZONE) + item.append(E.pubDate(pubdate.strftime(DATE_FORMAT))) + + return item + + +def generate_concerts(concerts_src, concerts_url, concerts_pubdates, lang): + with open(concerts_pubdates) as pubdates_file: + pubdates = json.load(pubdates_file) + + return tuple( + generate_concert(c, concerts_url, pubdates, lang) + for c in read_concerts(concerts_src) + ) + + +def main(concerts_src, feed_dst, concerts_pubdates, domain): + lang = guess_language(concerts_src) + text = LOCALIZED_TEXT[lang] + + url = f'https://{domain}' + index_url = urljoin(url, text['indexpath']) + concerts_url = urljoin(index_url, 'concerts.html') + + now_formatted = NOW.strftime(DATE_FORMAT) + + concerts = generate_concerts( + concerts_src, concerts_url, concerts_pubdates, lang + ) + + rss = E.rss( + E.channel( + E.title(text['title']), + E.link(index_url), + E.description(text['description']), + E.image( + E.url(urljoin(url, 'images/logo.svg')), + E.link(concerts_url), + ), + E.lastBuildDate(now_formatted), + E.pubDate(now_formatted), + E.language(lang), + *concerts, + ), + version='2.0', + ) + + indent(rss) + + with open(feed_dst, 'wb') as feed: + feed.write(tostring(rss, encoding='utf-8', xml_declaration=True)) + + +if __name__ == '__main__': + main(*argv[1:]) -- cgit v1.2.3 From a7b877da001054378e86cb32bc8a9467423f7e5d Mon Sep 17 00:00:00 2001 From: Kévin Le Gouguec Date: Wed, 23 Feb 2022 10:37:11 +0100 Subject: Remove superfluous whitespace from feeds --- admin/feeds/build-feed.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'admin/feeds/build-feed.py') diff --git a/admin/feeds/build-feed.py b/admin/feeds/build-feed.py index 5fe5c04..e835ad7 100755 --- a/admin/feeds/build-feed.py +++ b/admin/feeds/build-feed.py @@ -75,9 +75,6 @@ def join(sequence, joiner_factory): result.append(joiner_factory()) -CDATA_INDENT = 8*' ' - - def cdata_concert(concert, lang): formatters = DATE_FORMATTERS[lang] @@ -101,15 +98,17 @@ def cdata_concert(concert, lang): *(E.p(line) for line in concert.instructions.splitlines()), )) + # Do a silly dance to indent CDATA correctly. + for b in blocks: indent(b) html_blocks = (tostring(b, encoding='utf-8').decode() for b in blocks) - cdata = '\n' + '\n'.join(html_blocks) + '\n' - cdata = re.sub('^', CDATA_INDENT, cdata, flags=re.MULTILINE) + cdata = '\n'.join(html_blocks) + '\n' + cdata = re.sub('^', 8*' ', cdata, flags=re.MULTILINE) - return CDATA(cdata) + return CDATA('\n' + cdata) def generate_concert(concert, concerts_url, pubdates, lang): -- cgit v1.2.3 From a3b76e82d935d78e41aa54c3228dbd6fb9e36fc1 Mon Sep 17 00:00:00 2001 From: Kévin Le Gouguec Date: Wed, 23 Feb 2022 20:29:50 +0100 Subject: Fix instructions HTMLization & tidy up Thank Humanity for linters. --- Makefile | 2 +- admin/feeds/build-feed.py | 12 ++++++++---- build-concerts.py | 15 ++++++++------- en/feed.xml | 42 +++++++++++++++++++++++++++--------------- feed.xml | 43 ++++++++++++++++++++++++++++--------------- helpers.py | 2 ++ 6 files changed, 74 insertions(+), 42 deletions(-) (limited to 'admin/feeds/build-feed.py') diff --git a/Makefile b/Makefile index 055daf8..ca01374 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ scripts_folders = $(call dirnames,$(scripts)) all: site -feeds: $(feeds_folders) +feeds: ./admin/feeds/build-feeds.sh $(feeds_src) upload: site diff --git a/admin/feeds/build-feed.py b/admin/feeds/build-feed.py index e835ad7..e010b63 100755 --- a/admin/feeds/build-feed.py +++ b/admin/feeds/build-feed.py @@ -70,10 +70,12 @@ def join(sequence, joiner_factory): result.append(item) if i == len(sequence): - return result + break result.append(joiner_factory()) + return result + def cdata_concert(concert, lang): formatters = DATE_FORMATTERS[lang] @@ -89,13 +91,15 @@ def cdata_concert(concert, lang): E.p(formatters['time'](concert.time)), )) + pieces = touchup_plaintext(concert.pieces) + instructions = touchup_plaintext(concert.instructions) + blocks.extend(( E.p(*join(concert.address.splitlines(), E.br)), E.ol( - *(XML(f'
  • {touchup_plaintext(p)}
  • ') - for p in concert.pieces.splitlines()) + *(XML(f'
  • {line}
  • ') for line in pieces.splitlines()) ), - *(E.p(line) for line in concert.instructions.splitlines()), + *(XML(f'

    {line}

    ') for line in instructions.splitlines()), )) # Do a silly dance to indent CDATA correctly. diff --git a/build-concerts.py b/build-concerts.py index 4f55f12..6e99673 100755 --- a/build-concerts.py +++ b/build-concerts.py @@ -2,7 +2,6 @@ from datetime import datetime from pathlib import Path -import re from sys import argv from helpers import ( @@ -71,17 +70,19 @@ THUMBNAIL_TEMPLATE = '''\ def format_credits(illustration): - credits = illustration.source_name + attribution = illustration.source_name if illustration.source_link is not None: - credits = (f'' - f'{illustration.source_name}' - '') + attribution = ( + f'' + f'{illustration.source_name}' + '' + ) if illustration.license_info is not None: - credits += ' / ' + illustration.license_info.format() + attribution += ' / ' + illustration.license_info.format() - return credits + return attribution def format_thumbnail(concert, imgdir, lang): diff --git a/en/feed.xml b/en/feed.xml index 02cdf81..72d5c8e 100644 --- a/en/feed.xml +++ b/en/feed.xml @@ -8,8 +8,8 @@ https://quatuorbellefeuille.com/images/logo.svg https://quatuorbellefeuille.com/en/concerts.html - 23 Feb 2022 10:27 +0100 - 23 Feb 2022 10:27 +0100 + 23 Feb 2022 20:28 +0100 + 23 Feb 2022 20:28 +0100 en October 5 2019 in Le Buisson de Cadouin @@ -23,7 +23,8 @@
  • Mendelssohn quartet No. 3, Op. 44 No. 1
  • Ravel string quartet
  • -

    Information and reservation: https://arcadesinfo.com/évènement/quatuor-a-cordes-bellefeuille/

    +

    Information and reservation: https://arcadesinfo.com/évènement/quatuor-a-cordes-bellefeuille/ +

    ]]>
    @@ -38,7 +39,8 @@
  • Chostakovitch quartet No. 11
  • Ravel string quartet
  • -

    Information and reservation: https://musiqueaucoeurdesbastides.jimdofree.com/

    +

    Information and reservation: https://musiqueaucoeurdesbastides.jimdofree.com/ +

    ]]>
    @@ -53,7 +55,8 @@
  • Beethoven quartet No. 7, Op. 59 No. 1
  • Free admission without reservation, subject to the number of available places.

    -

    For more information, please contact quatuorbellefeuille@gmail.com

    +

    For more information, please contact quatuorbellefeuille@gmail.com +

    ]]>
    @@ -67,7 +70,8 @@
  • Beethoven string trio Op. 9 No. 3
  • Free admission without reservation, subject to the number of available places.

    -

    For more information, please contact quatuorbellefeuille@gmail.com

    +

    For more information, please contact quatuorbellefeuille@gmail.com +

    ]]>
    @@ -83,7 +87,8 @@
  • Brahms quartet Op. 51 No. 2
  • Ravel string quartet
  • -

    Information and reservation: https://weekenddemusiqueclassique.fr

    +

    Information and reservation: https://weekenddemusiqueclassique.fr +

    ]]> 19 Mar 2021 16:00 +0100
    @@ -100,7 +105,8 @@
  • Beethoven quartet No. 7, Op. 59 No. 1
  • Free admission without reservation, subject to the number of available places.

    -

    For more information, please contact quatuorbellefeuille@gmail.com

    +

    For more information, please contact quatuorbellefeuille@gmail.com +

    ]]> 19 Mar 2021 16:00 +0100 @@ -115,7 +121,8 @@
  • Ravel string quartet
  • Free admission without reservation, subject to the number of available places.

    -

    For more information, please contact quatuorbellefeuille@gmail.com

    +

    For more information, please contact quatuorbellefeuille@gmail.com +

    ]]> @@ -129,7 +136,8 @@
  • Bartók quartet No. 2
  • Free admission without reservation, subject to the number of available places.

    -

    For more information, please contact quatuorbellefeuille@gmail.com

    +

    For more information, please contact quatuorbellefeuille@gmail.com +

    ]]>
    @@ -144,7 +152,8 @@
  • Schubert quartettsatz
  • Brahms quartet Op. 51 No. 2
  • -

    For more information, please refer to: https://www.ledimoredelquartetto.eu/en/october-25-2021-le-grand-mello-mello-france/

    +

    For more information, please refer to: https://www.ledimoredelquartetto.eu/en/october-25-2021-le-grand-mello-mello-france/ +

    ]]>
    @@ -159,7 +168,8 @@
  • Brahms quartet Op. 51 No. 2
  • Ravel string quartet
  • -

    For more information, please refer to: https://www.ledimoredelquartetto.eu/en/october-28-2021-casa-st-germain-des-pres-paris/

    +

    For more information, please refer to: https://www.ledimoredelquartetto.eu/en/october-28-2021-casa-st-germain-des-pres-paris/ +

    ]]>
    @@ -175,7 +185,8 @@
  • Beethoven quartet No. 7, Op. 59 No. 1
  • Free admission without reservation, subject to the number of available places.

    -

    For more information, please contact quatuorbellefeuille@gmail.com

    +

    For more information, please contact quatuorbellefeuille@gmail.com +

    ]]> 23 Nov 2021 23:28 +0100
    @@ -191,7 +202,8 @@
  • Schubert quartettsatz
  • Ravel string quaret
  • -

    For more information, please refer to: https://www.ledimoredelquartetto.eu/en/december-31-2021-grand-bigard-castle-brussels-belgium/

    +

    For more information, please refer to: https://www.ledimoredelquartetto.eu/en/december-31-2021-grand-bigard-castle-brussels-belgium/ +

    ]]> @@ -208,7 +220,7 @@
  • Shostakovich quartet
  • Free admission, subject to the number of available places.

    -

    Contact info@pianorium.fr to make a reservation.

    +

    Contact info@pianorium.fr to make a reservation.

    ]]>
    diff --git a/feed.xml b/feed.xml index 2e69710..026e284 100644 --- a/feed.xml +++ b/feed.xml @@ -8,8 +8,8 @@ https://quatuorbellefeuille.com/images/logo.svg https://quatuorbellefeuille.com/concerts.html - 23 Feb 2022 10:27 +0100 - 23 Feb 2022 10:27 +0100 + 23 Feb 2022 20:27 +0100 + 23 Feb 2022 20:27 +0100 fr 5 octobre 2019 à Le Buisson de Cadouin @@ -23,7 +23,8 @@
  • Mendelssohn quatuor n°3, op.44 n°1
  • Ravel quatuor à cordes
  • -

    Informations et réservations : https://arcadesinfo.com/évènement/quatuor-a-cordes-bellefeuille/

    +

    Informations et réservations : https://arcadesinfo.com/évènement/quatuor-a-cordes-bellefeuille/ +

    ]]>
    @@ -38,7 +39,8 @@
  • Chostakovitch quatuor n°11
  • Ravel quatuor à cordes
  • -

    Informations et réservations : https://musiqueaucoeurdesbastides.jimdofree.com/

    +

    Informations et réservations : https://musiqueaucoeurdesbastides.jimdofree.com/ +

    ]]>
    @@ -53,7 +55,8 @@
  • Beethoven quatuor n°7, op.59 n°1
  • Entrée libre sans réservation dans la limite des places disponibles.

    -

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com

    +

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com +

    ]]>
    @@ -67,7 +70,8 @@
  • Beethoven trio à cordes op.9 n°3
  • Entrée libre sans réservation, dans la limite des places disponibles.

    -

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com

    +

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com +

    ]]>
    @@ -83,7 +87,8 @@
  • Brahms quatuor op.51 n°2
  • Ravel quatuor à cordes
  • -

    Informations et réservations : https://weekenddemusiqueclassique.fr

    +

    Informations et réservations : https://weekenddemusiqueclassique.fr +

    ]]> 19 Mar 2021 16:00 +0100
    @@ -100,7 +105,8 @@
  • Beethoven quatuor n°7, op.59 n°1
  • Entrée libre sans réservation, dans la limite des places disponibles.

    -

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com

    +

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com +

    ]]> 19 Mar 2021 16:00 +0100 @@ -115,7 +121,8 @@
  • Ravel quatuor à cordes
  • Entrée libre sans réservation, dans la limite des places disponibles.

    -

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com

    +

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com +

    ]]> @@ -129,7 +136,8 @@
  • Bartók quatuor n°2
  • Entrée libre sans réservation, dans la limite des places disponibles.

    -

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com

    +

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com +

    ]]>
    @@ -144,7 +152,8 @@
  • Schubert quartettsatz
  • Brahms quatuor op.51 n°2
  • -

    Pour plus d'informations, rendez-vous sur : https://www.ledimoredelquartetto.eu/en/october-25-2021-le-grand-mello-mello-france/

    +

    Pour plus d'informations, rendez-vous sur : https://www.ledimoredelquartetto.eu/en/october-25-2021-le-grand-mello-mello-france/ +

    ]]>
    @@ -159,7 +168,8 @@
  • Brahms quatuor op.51 n°2
  • Ravel quatuor à cordes
  • -

    Pour plus d'informations, rendez-vous sur : https://www.ledimoredelquartetto.eu/en/october-28-2021-casa-st-germain-des-pres-paris/

    +

    Pour plus d'informations, rendez-vous sur : https://www.ledimoredelquartetto.eu/en/october-28-2021-casa-st-germain-des-pres-paris/ +

    ]]>
    @@ -175,7 +185,8 @@
  • Beethoven quatuor n°7, op.59 n°1
  • Entrée libre sans réservation dans la limite des places disponibles.

    -

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com

    +

    Pour plus d'informations, merci de contacter : quatuorbellefeuille@gmail.com +

    ]]> 23 Nov 2021 23:28 +0100
    @@ -191,7 +202,8 @@
  • Schubert quartettsatz
  • Ravel quatuor à cordes
  • -

    Pour plus d'informations, rendez-vous sur : https://www.ledimoredelquartetto.eu/en/december-31-2021-grand-bigard-castle-brussels-belgium/

    +

    Pour plus d'informations, rendez-vous sur : https://www.ledimoredelquartetto.eu/en/december-31-2021-grand-bigard-castle-brussels-belgium/ +

    ]]> @@ -208,7 +220,8 @@
  • Chostakovitch quatuor
  • Entrée libre dans la limite des places disponibles.

    -

    Réservation possible à l'adresse suivante : info@pianorium.fr

    +

    Réservation possible à l'adresse suivante : info@pianorium.fr +

    ]]>
    diff --git a/helpers.py b/helpers.py index a6c4de2..11a13ca 100644 --- a/helpers.py +++ b/helpers.py @@ -43,6 +43,7 @@ _LICENSE_RE = re.compile( '('+'|'.join(_LICENSE_URLS.keys())+')' + ' ([0-9.]+)' ) + @dataclass class LicenseInfo: tag: str @@ -103,6 +104,7 @@ class Concert: def _optional(line): return f'(?:{line})?' + _CONCERT_LINES = ( r'QUAND : (?P