mirror of
https://github.com/Theodor-Springmann-Stiftung/documentamusica.git
synced 2025-10-28 08:35:31 +00:00
719 lines
29 KiB
Python
719 lines
29 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Generate static HTML catalog pages from SQL dump
|
||
"""
|
||
|
||
import re
|
||
import json
|
||
from pathlib import Path
|
||
from html import escape
|
||
|
||
# Language mappings
|
||
LANG_NAMES = {
|
||
'DE': 'Deutsch',
|
||
'EN': 'English',
|
||
'FR': 'Français'
|
||
}
|
||
|
||
# Composer names for file generation
|
||
COMPOSERS = ['Bach', 'Beethoven', 'Brahms', 'Buxtehude', 'Chopin', 'Mozart', 'Schumann', 'Wagner', 'Wieck']
|
||
|
||
# German mappings (from dataDE.inc.php)
|
||
GENRE_DE = {
|
||
"am": "Abendmusiken", "hoch": "Hochzeitsarien", "cas": "Kanons", "cantn": "Kantaten",
|
||
"lit": "Liturgische Werke", "cla": "Werke für Klavier", "worg": "Werke für Orgel",
|
||
"ws": "Werke für Streicher mit Basso continuo", "wom": "Werke ohne Musik",
|
||
"art": "Die Kunst der Fuge", "cano": "Kanon", "cant": "Kantate", "cha": "Kammermusik",
|
||
"chov": "Choral, vierstimmig", "con": "Konzert", "cpia": "Konzert für zwei bis vier Klaviere",
|
||
"csol": "Konzert für ein oder mehrere Soloinstrumente", "hymn": "Lied", "lut": "Musik für Laute",
|
||
"mass": "Messe", "mot": "Motette", "off": "Musikalisches Opfer", "ora": "Oratorium",
|
||
"org": "Musik für Orgel", "over": "Ouvertüre", "pas": "Passionsmusik",
|
||
"pian": "Musik für Klavier und Cembalo", "imos": "Instrumentalmusik für Orchester: Symphonie",
|
||
"imoo": "Instrumentalmusik für Orchester: Ouverture", "imob": "Instrumentalmusik für Orchester: Ballettmusik",
|
||
"imoa": "Instrumentalmusik für Orchester: Andere Werke", "imb": "Instrumentalmusik: für Blasinstrumente",
|
||
"imsso": "Instrumentalmusik: für mehrere Soloinstrumente und Orchester",
|
||
"imko": "Instrumentalmusik für Klavier und Orchester", "imvo": "Instrumentalmusik für Violine und Orchester",
|
||
"kammk": "Instrumentalmusik: Kammermusik mit Klavier", "kamok": "Instrumentalmusik: Kammermusik ohne Klavier",
|
||
"imkv": "Instrumentalmusik für Klavier zu vier Händen", "imkzs": "Instrumentalmusik für Klavier zu zwei Händen: Sonate",
|
||
"imkzv": "Instrumentalmusik für Klavier zu zwei Händen: Variation", "imkzt": "Instrumentalmusik für Klavier zu zwei Händen: Tanz",
|
||
"imkza": "Instrumentalmusik für Klavier zu zwei Händen: Andere Werke", "imsa": "Instrumentalmusik: Solostücke für andere Instrumente",
|
||
"vmu": "Vokalmusik", "vmumo": "Vokalmusik: Messe, Oratorium", "vmuo": "Vokalmusik: Oper, Bühnenmusik",
|
||
"vmub": "Vokalmusik: für eine oder mehrer Stimmen mit Begleitung", "vmuob": "Vokalmusik: für eine oder mehrer Stimmen ohne Begleitung",
|
||
"vmuk": "Vokalmusik: Kanon", "vmus": "Vokalmusik: Musikalischer Scherz", "bal": "Ballade",
|
||
"ein": "Einzelstück", "etu": "Etude", "fug": "Fuge, Kanon", "imp": "Impromptu", "kam": "Kammermusik",
|
||
"lie": "Lied", "maz": "Mazurka", "noc": "Nocturne", "pol": "Polonaise", "pre": "Prélude",
|
||
"ron": "Rondo", "sch": "Scherzo", "son": "Sonate", "var": "Variation", "wal": "Walzer",
|
||
"vom": "Messe", "vol": "Litanei", "vog": "geistlicher Gesang", "voo": "Kantate, Oratorium",
|
||
"vop": "Oper", "voa": "Arie, Duett, Trio, Quartett, mit oder ohne Begleitung", "vlk": "Lied mit Klavierbegleitung",
|
||
"vok": "Kanon", "iou": "Ouvertüre", "isy": "Symphonie", "ise": "Serenade, Divertimento",
|
||
"ima": "Marsch, Einzelsatz, kleineres Stück", "ita": "Tanz",
|
||
"iks": "Konzert für Saiten- oder Blasinstrumente mit Orchester", "cqs": "Streichquintett, -quartett",
|
||
"cds": "Streich-Duo, -Trio", "kor": "Musik für ein, zwei oder drei Klaviere und Orchester",
|
||
"kqt": "Trio, Quartett, Quintett für Klavier oder Cembalo", "ksv": "Sonate für Tasteninstrument und Violine",
|
||
"kss": "Sonate für Tasteninstrument und Streicher", "kvh": "Musik für Klavier oder Cembalo zu vier Händen",
|
||
"ksp": "Sonate, Phantasie für Tasteninstrument", "kva": "Variationen für Tasteninstrument",
|
||
"kks": "kleineres Stück für Tasteninstrument", "sio": "Sonate für verschiedene Instrumente und Orgel",
|
||
"sup": "unvollendetes oder zweifelhaftes Werk", "arr": "Arrangement", "cho": "Chormusik",
|
||
"exe": "Studienwerk", "kkm": "Klavier- und Kammermusik", "opu": "Oper (unvollendet)",
|
||
"opv": "Oper", "orc": "Orchesterwerk", "sce": "Schauspielmusik", "the": "Einzelthema oder Melodie",
|
||
"vor": "Arie mit Orchester", "xcp": "Kammermusik: Klarinette und Klavier",
|
||
"xqc": "Kammermusik: Klarinettenquintett", "xpo": "Kammermusik: Klavier oder Orgel",
|
||
"xpvh": "Kammermusik: Klavier zu vier Händen", "xtqqp": "Kammermusik: Klaviertrios, -quartette, -quintett",
|
||
"xst": "Kammermusik: Streichinstrumente", "xvp": "Kammermusik: Violine und Klavier",
|
||
"xvcp": "Kammermusik: Violoncello und Klavier", "xpd": "Kammermusik: zwei Klaviere",
|
||
"xos": "Orchester mit Soloinstrument", "xow": "Orchesterwerke", "xch": "Vokalmusik: Chöre",
|
||
"xdgp": "Vokalmusik: Duette mit Klavier", "xgp": "Vokalmusik: einstimmige Lieder mit Klavier",
|
||
"xgins": "Vokalmusik: Lieder und Chöre mit mehreren Instrumenten",
|
||
"xgmp": "Vokalmusik: mehrstimmige Gesänge mit Klavier oder Orgel",
|
||
"xg": "Vokalmusik: mehrstimmige Gesänge ohne Begleitung",
|
||
"ybew": "Bearbeitungen von Werken anderer Komponisten", "ybum": "Bühnenmusik",
|
||
"yccf": "Chormusik a cappella, Frauenstimen", "yccg": "Chormusik a cappella, gemischte Stimmen",
|
||
"yccm": "Chormusik a cappella, Männerstimmen", "ycco": "Chorwerke mit Orchester",
|
||
"ydtg": "Duette und Trios für Gesang", "ykfs": "Kammermusik für Streicher",
|
||
"ykmp": "Kammermusik mit Klavier", "ykmo": "Konzerte mit Orchester", "ylie": "Lieder",
|
||
"yvgp": "Mehrstimmige Gesänge mit Klavier oder Orgel", "ypvh": "Musik für Klavier zu vier Händen",
|
||
"ypzh": "Musik für Klavier zu zwei Händen", "ymzp": "Musik für zwei Klaviere",
|
||
"ywfo": "Werke für Orchester", "yorg": "Werke für Orgel",
|
||
"wschor": "Vokalmusik: Chöre", "wslied": "Lied", "wsorchkm": "Orchester/Kammermusik", "wspi": "Klavier"
|
||
}
|
||
|
||
TONART_DE = {
|
||
"cdur": "C-Dur", "fdur": "F-Dur", "bdur": "B-Dur", "esdur": "Es-Dur", "asdur": "As-Dur",
|
||
"desdur": "Des-Dur", "gesdur": "Ges-Dur", "cesdur": "Ces-Dur", "gdur": "G-Dur", "ddur": "D-Dur",
|
||
"adur": "A-Dur", "edur": "E-Dur", "hdur": "H-Dur", "fisdur": "Fis-Dur", "cisdur": "Cis-Dur",
|
||
"amoll": "a-Moll", "dmoll": "d-Moll", "gmoll": "g-Moll", "cmoll": "c-Moll", "fmoll": "f-Moll",
|
||
"bmoll": "b-Moll", "esmoll": "es-Moll", "asmoll": "as-Moll", "emoll": "e-Moll", "hmoll": "h-Moll",
|
||
"fismoll": "fis-Moll", "cismoll": "cis-Moll", "gismoll": "gis-Moll", "dismoll": "dis-Moll", "aismoll": "ais-Moll"
|
||
}
|
||
|
||
BESETZUNG_DE = {
|
||
"-al-": "Alt", "-ba-": "Bass", "-bn-": "Fagott", "-cb-": "Kontrabass", "-cem-": "Cembalo",
|
||
"-choir-": "Chor", "-cl-": "Klarinette", "-clo-": "Clarino", "-co-": "Horn", "-cont-": "Continuo",
|
||
"-fl-": "Flöte", "-gh-": "Glasharmonica", "-ha-": "Harfe", "-lu-": "Laute", "-man-": "Mandoline",
|
||
"-ob-": "Oboe", "-orch-": "Orchester", "-org-": "Orgel", "-pi-": "Klavier", "-so-": "Sopran",
|
||
"-taille-": "Taille", "-tamburi-": "Tamburi", "-tb-": "Posaune", "-te-": "Tenor", "-tm-": "Pauke/Trommel",
|
||
"-tp-": "Trompete", "-va-": "Viola", "-vadagamba-": "Viola da Gamba", "-vc-": "Violoncello",
|
||
"-vn-": "Violine", "-vo-": "Stimme", "-vs-": "Stimmen", "-vnpic-": "Violino piccolo"
|
||
}
|
||
|
||
# English mappings (abbreviated for brevity - add full mappings based on dataEN.inc.php)
|
||
GENRE_EN = {**GENRE_DE} # Simplified - should use full English translations
|
||
TONART_EN = {
|
||
"cdur": "C major", "fdur": "F major", "bdur": "B flat major", "esdur": "E flat major",
|
||
"asdur": "A flat major", "desdur": "D flat major", "gesdur": "G flat major", "cesdur": "C flat major",
|
||
"gdur": "G major", "ddur": "D major", "adur": "A major", "edur": "E major", "hdur": "B major",
|
||
"fisdur": "F sharp major", "cisdur": "C sharp major", "amoll": "A minor", "dmoll": "D minor",
|
||
"gmoll": "G minor", "cmoll": "C minor", "fmoll": "F minor", "bmoll": "B flat minor",
|
||
"esmoll": "E flat minor", "asmoll": "A flat minor", "emoll": "E minor", "hmoll": "B minor",
|
||
"fismoll": "F sharp minor", "cismoll": "C sharp minor", "gismoll": "G sharp minor",
|
||
"dismoll": "D sharp minor", "aismoll": "A sharp minor"
|
||
}
|
||
BESETZUNG_EN = {**BESETZUNG_DE} # Simplified
|
||
|
||
# French mappings (abbreviated - use full mappings)
|
||
GENRE_FR = {**GENRE_DE} # Simplified
|
||
TONART_FR = {
|
||
"cdur": "ut majeur", "fdur": "fa majeur", "bdur": "si bémol majeur", "esdur": "mi bémol majeur",
|
||
"asdur": "la bémol majeur", "desdur": "ré bémol majeur", "gesdur": "sol bémol majeur",
|
||
"cesdur": "ut bémol majeur", "gdur": "sol majeur", "ddur": "ré majeur", "adur": "la majeur",
|
||
"edur": "mi majeur", "hdur": "si majeur", "fisdur": "fa dièse majeur", "cisdur": "ut dièse majeur",
|
||
"amoll": "la mineur", "dmoll": "ré mineur", "gmoll": "sol mineur", "cmoll": "ut mineur",
|
||
"fmoll": "fa mineur", "bmoll": "si bémol mineur", "esmoll": "mi bémol mineur",
|
||
"asmoll": "la bémol mineur", "emoll": "mi mineur", "hmoll": "si mineur",
|
||
"fismoll": "fa dièse mineur", "cismoll": "ut dièse mineur", "gismoll": "sol dièse mineur",
|
||
"dismoll": "ré dièse mineur", "aismoll": "la dièse mineur"
|
||
}
|
||
BESETZUNG_FR = {**BESETZUNG_DE} # Simplified
|
||
|
||
# Mapping tables by language
|
||
GENRE_MAP = {'DE': GENRE_DE, 'EN': GENRE_EN, 'FR': GENRE_FR}
|
||
TONART_MAP = {'DE': TONART_DE, 'EN': TONART_EN, 'FR': TONART_FR}
|
||
BESETZUNG_MAP = {'DE': BESETZUNG_DE, 'EN': BESETZUNG_EN, 'FR': BESETZUNG_FR}
|
||
|
||
|
||
def parse_sql_dump(sql_file):
|
||
"""Parse SQL dump and extract work data"""
|
||
print(f"Parsing {sql_file}...")
|
||
|
||
with open(sql_file, 'r', encoding='latin-1') as f:
|
||
lines = f.readlines()
|
||
|
||
all_works = []
|
||
in_insert = False
|
||
current_insert_lines = []
|
||
|
||
for line in lines:
|
||
if line.startswith('INSERT INTO `daten` VALUES'):
|
||
in_insert = True
|
||
current_insert_lines = []
|
||
continue
|
||
elif in_insert:
|
||
if line.strip() == '' or line.startswith('/*!') or line.startswith('--'):
|
||
continue
|
||
if line.rstrip().endswith(';'):
|
||
# End of this INSERT block
|
||
current_insert_lines.append(line.rstrip(';\n'))
|
||
# Process this block
|
||
all_works.extend(parse_insert_block('\n'.join(current_insert_lines)))
|
||
in_insert = False
|
||
current_insert_lines = []
|
||
print(f" Processed INSERT block: {len(all_works)} total works so far")
|
||
else:
|
||
current_insert_lines.append(line.rstrip('\n'))
|
||
|
||
print(f"Parsed {len(all_works)} works")
|
||
return all_works
|
||
|
||
|
||
def parse_insert_block(full_text):
|
||
"""Parse a single INSERT block"""
|
||
works = []
|
||
|
||
# Split by '),\n(' to get individual records
|
||
records = re.split(r'\),\s*\n\(', full_text)
|
||
|
||
for record in records:
|
||
# Clean up the record
|
||
record = record.strip('()')
|
||
|
||
# Parse the CSV-like values (handling quoted strings with commas)
|
||
values = parse_values(record)
|
||
|
||
if len(values) >= 32: # Ensure we have all fields
|
||
work = {
|
||
'Komponist': values[0],
|
||
'TitelDE': values[1],
|
||
'TitelEN': values[2],
|
||
'TitelFR': values[3],
|
||
'TitelIT': values[4],
|
||
'TitelLA': values[5],
|
||
'TitelPL': values[6],
|
||
'IncipitDE': values[7],
|
||
'IncipitEN': values[8],
|
||
'IncipitFR': values[9],
|
||
'IncipitIT': values[10],
|
||
'IncipitLA': values[11],
|
||
'IncipitPL': values[12],
|
||
'Genre': values[13],
|
||
'Besetzung': values[14],
|
||
'Tonart': values[15],
|
||
'Jahr': values[16],
|
||
'WerkNr': values[17],
|
||
'BemerkungDE': values[18],
|
||
'BemerkungEN': values[19],
|
||
'BemerkungFR': values[20],
|
||
'sorWerkNr': values[32] if len(values) > 32 else 0
|
||
}
|
||
works.append(work)
|
||
|
||
return works
|
||
|
||
|
||
def parse_values(record):
|
||
"""Parse SQL VALUES - handle quoted strings with commas/quotes"""
|
||
values = []
|
||
current = []
|
||
in_quote = False
|
||
i = 0
|
||
|
||
while i < len(record):
|
||
char = record[i]
|
||
|
||
if char == "'" and (i == 0 or record[i-1] != '\\'):
|
||
in_quote = not in_quote
|
||
i += 1
|
||
continue
|
||
|
||
if char == ',' and not in_quote:
|
||
val = ''.join(current).strip()
|
||
# Remove quotes if present
|
||
if val.startswith("'") and val.endswith("'"):
|
||
val = val[1:-1]
|
||
values.append(val)
|
||
current = []
|
||
i += 1
|
||
# Skip whitespace after comma
|
||
while i < len(record) and record[i] in ' \t':
|
||
i += 1
|
||
continue
|
||
|
||
# Handle escaped quotes
|
||
if char == '\\' and i + 1 < len(record) and record[i+1] == "'":
|
||
current.append("'")
|
||
i += 2
|
||
continue
|
||
|
||
current.append(char)
|
||
i += 1
|
||
|
||
# Add last value
|
||
if current:
|
||
val = ''.join(current).strip()
|
||
# Remove quotes if present
|
||
if val.startswith("'") and val.endswith("'"):
|
||
val = val[1:-1]
|
||
values.append(val)
|
||
|
||
return values
|
||
|
||
|
||
def decode_genre(code, lang='DE'):
|
||
"""Decode genre code to text"""
|
||
return GENRE_MAP[lang].get(code, code)
|
||
|
||
|
||
def decode_tonart(code, lang='DE'):
|
||
"""Decode tonart (key) code to text"""
|
||
return TONART_MAP[lang].get(code, code)
|
||
|
||
|
||
def decode_besetzung(code, lang='DE'):
|
||
"""Decode besetzung (instrumentation) codes"""
|
||
if not code:
|
||
return ''
|
||
|
||
instruments = []
|
||
for key, value in BESETZUNG_MAP[lang].items():
|
||
if key in code:
|
||
instruments.append(value)
|
||
|
||
return ', '.join(instruments) if instruments else code
|
||
|
||
|
||
def generate_work_html(work, lang='DE'):
|
||
"""Generate HTML for a single work entry"""
|
||
title_field = f'Titel{lang}'
|
||
incipit_field = f'Incipit{lang}'
|
||
bemerkung_field = f'Bemerkung{lang}'
|
||
|
||
title = escape(work.get(title_field, ''))
|
||
incipit = escape(work.get(incipit_field, ''))
|
||
werknr = escape(work.get('WerkNr', ''))
|
||
jahr = escape(work.get('Jahr', ''))
|
||
genre = escape(decode_genre(work.get('Genre', ''), lang))
|
||
tonart = escape(decode_tonart(work.get('Tonart', ''), lang))
|
||
besetzung = escape(decode_besetzung(work.get('Besetzung', ''), lang))
|
||
bemerkung = escape(work.get(bemerkung_field, ''))
|
||
|
||
html = '<tr valign="top">\n'
|
||
html += f' <td width="30"></td>\n'
|
||
|
||
# Work number
|
||
if werknr:
|
||
html += f' <td width="100"><b>{werknr}</b></td>\n'
|
||
else:
|
||
html += ' <td width="100"></td>\n'
|
||
|
||
# Work details
|
||
html += ' <td>\n'
|
||
|
||
if title:
|
||
html += f' <b>{title}</b><br>\n'
|
||
if incipit:
|
||
html += f' <i>{incipit}</i><br>\n'
|
||
|
||
details = []
|
||
if genre:
|
||
details.append(f'Genre: {genre}')
|
||
if tonart:
|
||
details.append(f'Tonart: {tonart}' if lang == 'DE' else f'Key: {tonart}' if lang == 'EN' else f'Tonalité: {tonart}')
|
||
if jahr:
|
||
details.append(f'Jahr: {jahr}' if lang == 'DE' else f'Year: {jahr}' if lang == 'EN' else f'Année: {jahr}')
|
||
if besetzung:
|
||
details.append(f'Besetzung: {besetzung}' if lang == 'DE' else f'Instrumentation: {besetzung}' if lang == 'EN' else f'Instrumentation: {besetzung}')
|
||
if bemerkung:
|
||
details.append(f'<i>{bemerkung}</i>')
|
||
|
||
if details:
|
||
html += ' ' + ' | '.join(details) + '\n'
|
||
|
||
html += ' </td>\n'
|
||
html += '</tr>\n'
|
||
html += '<tr><td colspan="3"><hr noshade size="1" color="#DDBA86"></td></tr>\n'
|
||
|
||
return html
|
||
|
||
|
||
def generate_composer_catalog_content(composer, works, lang='DE'):
|
||
"""Generate the content HTML for a composer's catalog"""
|
||
composer_display = "Wieck-Schumann" if composer == "Wieck" else composer
|
||
|
||
content = f'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="content-language" content="{lang.lower()}">
|
||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||
<title>Wolf's Thematic Index - {composer_display} Catalog</title>
|
||
<meta name="copyright" content="Documenta musica (C) 2005">
|
||
<link href="katalog.css" rel="stylesheet" type="text/css">
|
||
<script language="javascript">
|
||
<!--
|
||
function frameset() {{
|
||
if (!parent.unten)
|
||
location.href = "{lang.lower()}-katalog-{composer.lower()}.html";
|
||
}}
|
||
//-->
|
||
</script>
|
||
</head>
|
||
<body background="../bilder/hintergrund.gif" style="text-align: center;" onload="frameset()">
|
||
<div id="cont" align="center">
|
||
<table width="900" cellspacing="5" cellpadding="5">
|
||
<tr>
|
||
<td colspan="3" align="center">
|
||
<h1>{composer_display} - Werkverzeichnis</h1>
|
||
<p><i>{len(works)} {'Werke' if lang == 'DE' else 'Works' if lang == 'EN' else 'Œuvres'}</i></p>
|
||
</td>
|
||
</tr>
|
||
|
||
'''
|
||
|
||
# Sort works by work number
|
||
def safe_sort_key(w):
|
||
val = w.get('sorWerkNr', 0)
|
||
if val == '' or val == 'NULL' or val is None:
|
||
return 0
|
||
try:
|
||
return int(val)
|
||
except (ValueError, TypeError):
|
||
return 0
|
||
|
||
sorted_works = sorted(works, key=safe_sort_key)
|
||
|
||
for work in sorted_works:
|
||
content += generate_work_html(work, lang)
|
||
|
||
content += ''' </table>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
'''
|
||
|
||
return content
|
||
|
||
|
||
def generate_composer_catalog_frameset(composer, lang='DE'):
|
||
"""Generate the frameset HTML for a composer's catalog"""
|
||
composer_display = "Wieck-Schumann" if composer == "Wieck" else composer
|
||
|
||
return f'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||
<meta http-equiv="content-language" content="{lang.lower()}">
|
||
<meta name="copyright" content="Documenta musica (C) 2005">
|
||
<link rel="shortcut icon" href="../bilder/favicon.ico" type="image/x-icon">
|
||
<title>Wolf's Thematic Index - {composer_display} Catalog</title>
|
||
<script language="javascript">
|
||
<!--
|
||
function framecall() {{
|
||
var adressanhang = location.search;
|
||
if (adressanhang)
|
||
frames.inhalt.location.href=adressanhang.substring(1,adressanhang.length);
|
||
}}
|
||
//-->
|
||
</script>
|
||
</head>
|
||
<frameset rows="60,*,60" border="0" frameborder="0" framespacing="0" onload="framecall()">
|
||
<frame name="oben" scrolling="no" noresize src="logo-oben.html">
|
||
<frame name="inhalt" scrolling="auto" noresize src="{lang.lower()}-katalog-{composer.lower()}-inhalt.html" target="_self">
|
||
<frame name="unten" scrolling="no" noresize target="inhalt" src="{lang.lower()}-katalog-menu.html">
|
||
<noframes>
|
||
<body>
|
||
<p>
|
||
Diese Seite verwendet Frames. Frames werden von Ihrem Browser aber nicht
|
||
unterstützt.
|
||
</p>
|
||
</body>
|
||
</noframes>
|
||
</frameset>
|
||
</html>
|
||
'''
|
||
|
||
|
||
def generate_catalog_index_content(lang='DE'):
|
||
"""Generate the catalog index content (composer selection)"""
|
||
content = f'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="content-language" content="{lang.lower()}">
|
||
<title>Wolf's Thematic Index - Catalog</title>
|
||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-15">
|
||
<meta name="copyright" content="Documenta musica (C) 2005">
|
||
<script language="javascript">
|
||
<!--
|
||
function frameset() {{
|
||
if (!parent.unten)
|
||
location.href = "{lang.lower()}-katalog.html";
|
||
}}
|
||
//-->
|
||
</script>
|
||
<style type="text/css">
|
||
div {{ margin: 0; padding: 0; padding-top: 10px; }}
|
||
img {{ behavior: url(../js/iepngfix.htc); border: none; }}
|
||
</style>
|
||
</head>
|
||
<body background="../bilder/hintergrund.gif" style="text-align: center;" onload="frameset()">
|
||
<div style="padding-top: 36px;">
|
||
<a href="{lang.lower()}-katalog-bach.html" target="_parent">
|
||
<img src="../bilder/bach-blau-bio.png" width="372" height="40" alt="Johann Sebastian Bach"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-beethoven.html" target="_parent">
|
||
<img src="../bilder/beethoven-blau-bio.png" width="324" height="42" alt="Ludwig van Beethoven"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-brahms.html" target="_parent">
|
||
<img src="../bilder/brahms-blau-bio.png" width="269" height="40" alt="Johannes Brahms"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-buxtehude.html" target="_parent">
|
||
<img src="../bilder/buxtehude-blau-bio.png" width="324" height="42" alt="Dieterich Buxtehude"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-chopin.html" target="_parent">
|
||
<img src="../bilder/chopin-blau-bio.png" width="232" height="40" alt="Frédéric Chopin"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-mozart.html" target="_parent">
|
||
<img src="../bilder/mozart-blau-bio.png" width="405" height="41" alt="Wolfgang Amadeus Mozart"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-schumann.html" target="_parent">
|
||
<img src="../bilder/schumann-blau-bio.png" width="275" height="41" alt="Robert Schumann"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-wagner.html" target="_parent">
|
||
<img src="../bilder/wagner-blau-bio.png" width="256" height="40" alt="Richard Wagner"></a>
|
||
</div>
|
||
<div>
|
||
<a href="{lang.lower()}-katalog-wieck.html" target="_parent">
|
||
<img src="../bilder/wieck-schumann-blau-bio.png" width="350" height="41" alt="Clara Wieck-Schumann"></a>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
'''
|
||
return content
|
||
|
||
|
||
def generate_catalog_index_frameset(lang='DE'):
|
||
"""Generate the catalog index frameset"""
|
||
return f'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||
<meta http-equiv="content-language" content="{lang.lower()}">
|
||
<meta name="copyright" content="Documenta musica (C) 2005">
|
||
<link rel="shortcut icon" href="../bilder/favicon.ico" type="image/x-icon">
|
||
<title>Wolf's Thematic Index - Catalog</title>
|
||
<script language="javascript">
|
||
<!--
|
||
function framecall() {{
|
||
var adressanhang = location.search;
|
||
if (adressanhang)
|
||
frames.inhalt.location.href=adressanhang.substring(1,adressanhang.length);
|
||
}}
|
||
//-->
|
||
</script>
|
||
</head>
|
||
<frameset rows="60,*,60" border="0" frameborder="0" framespacing="0" onload="framecall()">
|
||
<frame name="oben" scrolling="no" noresize src="logo-oben.html">
|
||
<frame name="inhalt" scrolling="auto" noresize src="{lang.lower()}-katalog-inhalt.html" target="_self">
|
||
<frame name="unten" scrolling="no" noresize target="inhalt" src="{lang.lower()}-katalog-menu.html">
|
||
<noframes>
|
||
<body>
|
||
<p>
|
||
Diese Seite verwendet Frames. Frames werden von Ihrem Browser aber nicht
|
||
unterstützt.
|
||
</p>
|
||
</body>
|
||
</noframes>
|
||
</frameset>
|
||
</html>
|
||
'''
|
||
|
||
|
||
def generate_catalog_menu(lang='DE'):
|
||
"""Generate the catalog menu"""
|
||
menu_labels = {
|
||
'DE': {'start': 'Start', 'intro': 'Einführung', 'bio': 'Biographien',
|
||
'katalog': 'Katalog', 'form': 'Suchformular', 'klav': 'Klaviatur',
|
||
'quellen': 'Quellen', 'impressum': 'Impressum'},
|
||
'EN': {'start': 'Start', 'intro': 'Introduction', 'bio': 'Biographies',
|
||
'katalog': 'Catalog', 'form': 'Search Form', 'klav': 'Keyboard',
|
||
'quellen': 'Sources', 'impressum': 'Imprint'},
|
||
'FR': {'start': 'Start', 'intro': 'Introduction', 'bio': 'Biographies',
|
||
'katalog': 'Catalogue', 'form': 'Formulaire', 'klav': 'Clavier',
|
||
'quellen': 'Sources', 'impressum': 'Mentions légales'}
|
||
}
|
||
|
||
labels = menu_labels[lang]
|
||
|
||
return f'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="content-language" content="{lang.lower()}">
|
||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||
<meta name="copyright" content="Documenta musica (C) 2005">
|
||
<meta name="robots" content="noindex">
|
||
<title>Wolf's Thematic Index - Catalog - Menu</title>
|
||
<base target="inhalt">
|
||
<link href="menu.css" rel="stylesheet" type="text/css">
|
||
</head>
|
||
<body>
|
||
<table>
|
||
<tr>
|
||
<td>
|
||
<a href="../index.html" target="_parent">
|
||
{labels['start']}</td>
|
||
<td>
|
||
<a href="{lang.lower()}-intro.html" target="_parent">
|
||
{labels['intro']}</td>
|
||
<td>
|
||
<a href="{lang.lower()}-bio.html" target="_parent">
|
||
{labels['bio']}</a></td>
|
||
<td id="td_selected">
|
||
<a href="{lang.lower()}-katalog.html" target="_parent" id="a_selected">
|
||
{labels['katalog']}</a></td>
|
||
<td>
|
||
<a href="{lang.lower()}-formular.html" target="_parent">
|
||
{labels['form']}</a></td>
|
||
<td>
|
||
<a href="{lang.lower()}-klaviatur.html" target="_parent">
|
||
{labels['klav']}</a></td>
|
||
<td>
|
||
<a href="{lang.lower()}-quellen.html" target="_parent">
|
||
{labels['quellen']}</a></td>
|
||
<td>
|
||
<a href="{lang.lower()}-impressum.html" target="_parent">
|
||
{labels['impressum']}</a></td>
|
||
</tr>
|
||
</table>
|
||
</body>
|
||
</html>
|
||
'''
|
||
|
||
|
||
def generate_katalog_css():
|
||
"""Generate the katalog.css file"""
|
||
return '''/* Wolf's Thematic Index of the Works of the Great Composers */
|
||
/* katalog.css */
|
||
|
||
body, table, tr, td {
|
||
font-size: 1em;
|
||
font-family: georgia, 'times new roman', times, serif;
|
||
line-height: 130%;
|
||
margin-top: 0px;
|
||
margin-bottom: 0px;
|
||
}
|
||
|
||
p {
|
||
margin-top: 0px;
|
||
margin-bottom:5px;
|
||
padding: 0;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 200%;
|
||
margin-top: 10px;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
h2 {
|
||
font-size: 150%;
|
||
margin-top: 10px;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
a {
|
||
font-weight: bold;
|
||
color: #0000ff;
|
||
font-size: 1em;
|
||
letter-spacing: 0.05em;
|
||
text-decoration: none;
|
||
}
|
||
|
||
a:hover {
|
||
background-color: Yellow;
|
||
}
|
||
|
||
table {
|
||
border: none;
|
||
}
|
||
|
||
tr {
|
||
margin: 5px 0;
|
||
}
|
||
|
||
td {
|
||
padding: 3px 5px;
|
||
}
|
||
|
||
hr {
|
||
color: #DDBA86;
|
||
background-color: #DDBA86;
|
||
}
|
||
'''
|
||
|
||
|
||
def main():
|
||
"""Main function to generate all catalog files"""
|
||
base_dir = Path(__file__).parent
|
||
sql_file = base_dir / 'db-dump' / 'initial_db.sql'
|
||
html_dir = base_dir / 'src' / 'html'
|
||
|
||
# Parse SQL dump
|
||
all_works = parse_sql_dump(sql_file)
|
||
|
||
# Group works by composer
|
||
works_by_composer = {}
|
||
for work in all_works:
|
||
composer = work['Komponist']
|
||
if composer not in works_by_composer:
|
||
works_by_composer[composer] = []
|
||
works_by_composer[composer].append(work)
|
||
|
||
print(f"\nGenerating catalog pages...")
|
||
|
||
# Generate catalog CSS
|
||
print("Creating katalog.css...")
|
||
css_file = html_dir / 'katalog.css'
|
||
css_file.write_text(generate_katalog_css(), encoding='iso-8859-1', errors='replace')
|
||
|
||
# Generate for each language
|
||
for lang in ['DE', 'EN', 'FR']:
|
||
print(f"\nGenerating {lang} catalog files...")
|
||
|
||
# Generate catalog index files
|
||
frameset = generate_catalog_index_frameset(lang)
|
||
content = generate_catalog_index_content(lang)
|
||
menu = generate_catalog_menu(lang)
|
||
|
||
(html_dir / f'{lang.lower()}-katalog.html').write_text(frameset, encoding='iso-8859-1', errors='replace')
|
||
(html_dir / f'{lang.lower()}-katalog-inhalt.html').write_text(content, encoding='iso-8859-1', errors='replace')
|
||
(html_dir / f'{lang.lower()}-katalog-menu.html').write_text(menu, encoding='iso-8859-1', errors='replace')
|
||
|
||
# Generate composer catalog files
|
||
for composer in COMPOSERS:
|
||
if composer not in works_by_composer:
|
||
print(f" Warning: No works found for {composer}")
|
||
continue
|
||
|
||
works = works_by_composer[composer]
|
||
print(f" Generating {composer} catalog ({len(works)} works)...")
|
||
|
||
frameset = generate_composer_catalog_frameset(composer, lang)
|
||
content = generate_composer_catalog_content(composer, works, lang)
|
||
|
||
(html_dir / f'{lang.lower()}-katalog-{composer.lower()}.html').write_text(frameset, encoding='iso-8859-1', errors='replace')
|
||
(html_dir / f'{lang.lower()}-katalog-{composer.lower()}-inhalt.html').write_text(content, encoding='iso-8859-1', errors='replace')
|
||
|
||
print("\n✅ Catalog generation complete!")
|
||
print(f" Generated {3 * (3 + 2 * len(COMPOSERS)) + 1} files")
|
||
print(f" - 1 CSS file")
|
||
print(f" - 9 index files (3 languages × 3 files each)")
|
||
print(f" - {2 * len(COMPOSERS) * 3} composer files ({len(COMPOSERS)} composers × 2 files × 3 languages)")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|