from dataclasses import dataclass from datetime import datetime from operator import attrgetter from os import path from pathlib import Path import re from typing import Iterator, Optional def guess_language(filename): parent = str(Path(filename).parent) if parent == '.': return 'fr' return parent def relative_path(*, to, ref): # pathlib.Path(x).relative_to(y) cannot handle y not being under x, # os.path.dirname('x') yields '' rather than '.'. # 😮‍💨 return path.relpath(to, Path(ref).parent) _LICENSE_URLS = { 'CC0': 'https://creativecommons.org/publicdomain/zero', 'CC BY': 'https://creativecommons.org/licenses/by', 'CC BY-SA': 'https://creativecommons.org/licenses/by-sa', } _LICENSE_RE = re.compile( '('+'|'.join(_LICENSE_URLS.keys())+')' + ' ([0-9.]+)' ) @dataclass class LicenseInfo: tag: str version: str @classmethod def deserialize(cls, info): if info is None: return None return cls(*_LICENSE_RE.fullmatch(info).groups()) def format(self): url = f'{_LICENSE_URLS[self.tag]}/{self.version}/' return f'{self.tag}' @dataclass class Illustration: file: str alt_text: str source_name: str source_link: Optional[str] license_info: Optional[LicenseInfo] @classmethod def deserialize(cls, d): return cls(d['pic_file'], d['pic_alt'], d['pic_src'], d['pic_link'], LicenseInfo.deserialize(d['pic_license'])) @dataclass class Concert: time: datetime place: str address: str pieces: Iterator[str] instructions: str illustration: Illustration warning: Optional[str] @classmethod def deserialize(cls, d): return cls( time=datetime.strptime(d['time'], '%d/%m/%Y %Hh%M'), place=d['place'], address=d['address'], pieces=d['pieces'], instructions=d['instructions'], illustration=Illustration.deserialize(d), warning=d['warning'] ) def _optional(line): return f'(?:{line})?' _CONCERT_LINES = ( r'QUAND : (?P