quatuorbellefeuille.com

Content, build scripts and admin scripts for the Bellefeuille Quartet website.
git clone https://git.kevinlegouguec.net/quatuorbellefeuille.com
Log | Files | Refs

build-concerts.py (5708B)


      1 #!/usr/bin/env python3
      2 
      3 from collections import OrderedDict
      4 from datetime import datetime
      5 from pathlib import Path
      6 from sys import argv
      7 
      8 from helpers import (
      9     DATE_FORMATTERS,
     10     guess_language,
     11     read_concerts,
     12     relative_path,
     13     split_concerts,
     14     tmplocale,
     15     touchup_plaintext,
     16 )
     17 
     18 
     19 # TODO: change some jargon:
     20 # - event => concert
     21 # - canceled => warning
     22 
     23 
     24 LOCALIZED_TEXT = {
     25     'en': {
     26         'past': 'Past concerts',
     27         'next': 'Next concerts',
     28         'alt': 'Illustration:',
     29         'hint': 'Click on a concert to obtain more information.',
     30     },
     31     'fr': {
     32         'past': 'Concerts passés',
     33         'next': 'Prochains concerts',
     34         'alt': 'Illustration :',
     35         'hint': "Cliquez sur un concert pour obtenir plus d'informations.",
     36     }
     37 }
     38 
     39 THUMBNAILS_TEMPLATE = '''\
     40   <h1>{heading}</h1>
     41   <div class="events {time}">
     42 {thumbnails}
     43   </div>\
     44 '''
     45 
     46 THUMBNAIL_TEMPLATE = '''\
     47     <div class="{eventclasses}">
     48       <a class="thumbnail" href="#{eventid}">
     49         <img {pic_attributes}>
     50         <p class="summary">
     51           {summary}
     52         </p>
     53       </a>
     54       <div class="credits">
     55         <span>
     56           {credits}
     57         </span>
     58       </div>
     59     </div>\
     60 '''
     61 
     62 
     63 def format_credits(illustration):
     64     attribution = illustration.source_name
     65 
     66     if illustration.source_link is not None:
     67         attribution = (
     68             f'<a href="{illustration.source_link}" target="_blank">'
     69             f'{illustration.source_name}'
     70             '</a>'
     71         )
     72 
     73     if illustration.license_info is not None:
     74         attribution += ' / ' + illustration.license_info.format()
     75 
     76     return attribution
     77 
     78 
     79 def format_thumbnail(concert, imgdir, lang):
     80     eventclasses = ('event',)
     81     with tmplocale(lang):
     82         day = f'{concert.time.day} {concert.time.strftime("%B %Y")}'
     83     summary = f'{concert.place}<br>{day}'
     84 
     85     if concert.warning is not None:
     86         eventclasses += ('canceled',)
     87         summary = (f'<span class="canceled">{concert.warning}</span>\n'
     88                    f'          {summary}')
     89 
     90     alt_prefix = LOCALIZED_TEXT[lang]['alt']
     91 
     92     pic_attributes = OrderedDict((
     93         ('src', Path(imgdir, 'concerts', concert.illustration.file)),
     94         ('alt', f'{alt_prefix} {concert.illustration.alt_text}'),
     95         ('style', concert.illustration.style),
     96     ))
     97 
     98     return THUMBNAIL_TEMPLATE.format_map({
     99         'eventclasses': ' '.join(eventclasses),
    100         'eventid': f'concert-{concert.time.strftime("%F")}',
    101         'pic_attributes': ' '.join(
    102             f'{key}="{value}"'
    103             for key, value in pic_attributes.items()
    104             if value is not None
    105         ),
    106         'summary': summary,
    107         'credits': format_credits(concert.illustration)
    108     })
    109 
    110 
    111 def print_thumbnails_section(concerts, imgdir, section, lang):
    112     if not concerts:
    113         return
    114 
    115     thumbnails = '\n'.join(
    116         format_thumbnail(c, imgdir, lang) for c in concerts
    117     )
    118 
    119     print(THUMBNAILS_TEMPLATE.format(heading=LOCALIZED_TEXT[lang][section],
    120                                      time=section,
    121                                      thumbnails=thumbnails))
    122 
    123 
    124 def print_thumbnails(concerts, imgdir, lang):
    125     today = datetime.fromordinal(
    126         datetime.today().date().toordinal()
    127     )
    128     past_concerts, next_concerts = split_concerts(concerts, today)
    129 
    130     print('<div id="event-list">')
    131     print_thumbnails_section(next_concerts, imgdir, 'next', lang)
    132     print_thumbnails_section(list(reversed(past_concerts)), imgdir, 'past', lang)
    133     print('</div>')
    134 
    135 
    136 DETAILS_TEMPLATE = '''\
    137   <div class="{concertclasses}" id="{concertid}">
    138 {details}
    139   </div>\
    140 '''
    141 
    142 
    143 def detail_block(tag, classes, content):
    144     opener = f'<{tag} class="{" ".join(classes)}">'
    145     closer = f'</{tag}>'
    146 
    147     if isinstance(content, str):
    148         return f'    {opener}{content}{closer}'
    149 
    150     return '\n'.join((
    151         '    '+opener,
    152         *('      '+line for line in content),
    153         '    '+closer,
    154     ))
    155 
    156 
    157 def break_lines(lines):
    158     return tuple(line+'<br>' for line in lines[:-1]) + (lines[-1],)
    159 
    160 
    161 def print_concert_details(concert, lang):
    162     concert_id = f'concert-{concert.time.strftime("%F")}'
    163     classes = ('details',)
    164     blocks = []
    165 
    166     if concert.warning is not None:
    167         classes += ('canceled',)
    168         blocks.append(
    169             detail_block('p', ('canceled',), concert.warning)
    170         )
    171 
    172     with tmplocale(lang):
    173         day = DATE_FORMATTERS[lang]['date'](concert.time)
    174     hour = DATE_FORMATTERS[lang]['time'](concert.time)
    175 
    176     address_lines = break_lines(concert.address.splitlines())
    177     piece_list = tuple(
    178         f'<li>{touchup_plaintext(line)}</li>'
    179         for line in concert.pieces.splitlines()
    180     )
    181 
    182     blocks.extend((
    183         detail_block('p', ('detail', 'date'), day),
    184         detail_block('p', ('detail', 'time'), hour),
    185         detail_block('p', ('detail', 'place'), address_lines),
    186         detail_block('ol', ('detail', 'program'), piece_list),
    187     ))
    188 
    189     instructions = [
    190         f'    <p>{touchup_plaintext(line)}</p>'
    191         for line in concert.instructions.splitlines() if line
    192     ]
    193 
    194     print(f'  <div class="{" ".join(classes)}" id="{concert_id}">')
    195     print('\n'.join(blocks+instructions))
    196     print('  </div>')
    197 
    198 
    199 def print_details(concerts, lang):
    200     print('<div id="event-details">')
    201     print(f'  <p class="hint">{LOCALIZED_TEXT[lang]["hint"]}</p>')
    202 
    203     for c in concerts:
    204         print_concert_details(c, lang)
    205 
    206     print('</div>')
    207 
    208 
    209 def main(concerts_src):
    210     imgdir = relative_path(to='images', ref=concerts_src)
    211     lang = guess_language(concerts_src)
    212 
    213     concerts = read_concerts(concerts_src)
    214     print_thumbnails(concerts, imgdir, lang)
    215     print_details(concerts, lang)
    216 
    217 
    218 if __name__ == '__main__':
    219     main(argv[1])