pmi-site/build.py

172 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import markdown
import shutil
from pathlib import Path
TEMPLATE = """<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title} | ФМИ</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js" onload="renderMathInElement(document.body);"></script>
<style>
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.6; color: #333; }}
header {{ border-bottom: 2px solid #003366; padding-bottom: 10px; margin-bottom: 30px; }}
h1 {{ color: #003366; }}
h2 {{ color: #004080; border-bottom: 1px solid #eee; padding-bottom: 5px; margin-top: 30px; }}
h3 {{ color: #0055a4; margin-top: 25px; }}
nav {{ margin-bottom: 30px; }}
nav a {{ margin-right: 20px; text-decoration: none; color: #003366; font-weight: bold; font-size: 1.1em; }}
nav a:hover {{ text-decoration: underline; }}
.person {{ margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px dashed #ddd; }}
.person-name {{ font-weight: bold; font-size: 1.1em; color: #222; }}
.person-name a {{ color: #003366; text-decoration: none; }}
.person-name a:hover {{ text-decoration: underline; }}
.person-details {{ color: #555; font-size: 0.95em; }}
.back-link {{ display: inline-block; margin-bottom: 20px; color: #003366; text-decoration: none; }}
.back-link:hover {{ text-decoration: underline; }}
</style>
</head>
<body>
<header>
<h1>Физико-математический институт</h1>
<nav>
<a href="/">Главная</a>
<a href="/news.html">Новости</a>
<a href="/staff.html">Сотрудники</a>
</nav>
</header>
<main>
{content}
</main>
<footer style="margin-top: 50px; border-top: 1px solid #ccc; padding-top: 20px; font-size: 0.8em; color: #666;">
© 2026 Физико-математический институт Коми НЦ УрО РАН
</footer>
</body>
</html>"""
def parse_front_matter(text):
"""Простой парсер для извлечения метаданных из начала файла"""
if text.strip().startswith('---'):
parts = text.split('---', 2)
if len(parts) >= 3:
meta = {}
for line in parts[1].strip().split('\n'):
if ':' in line:
key, val = line.split(':', 1)
meta[key.strip()] = val.strip().strip('"')
return meta, parts[2].strip()
return {}, text.strip()
def build_page(md_path, html_path, title):
with open(md_path, 'r', encoding='utf-8') as f:
meta, md_text = parse_front_matter(f.read())
html_content = markdown.markdown(md_text, extensions=['fenced_code', 'tables'])
with open(html_path, 'w', encoding='utf-8') as f:
f.write(TEMPLATE.format(title=title, content=html_content))
def main():
Path('public').mkdir(exist_ok=True)
Path('public/news').mkdir(exist_ok=True)
Path('public/staff').mkdir(exist_ok=True)
# 1. Главная страница
if Path('content/index.md').exists():
build_page(Path('content/index.md'), 'public/index.html', 'Главная')
# 2. Страница новостей
news_files = sorted(Path('content/news').glob('*.md'), reverse=True)
news_links = "".join([f'<li><a href="news/{f.stem}.html">{f.stem.replace("-", " ").title()}</a></li>' for f in news_files])
with open('public/news.html', 'w', encoding='utf-8') as f:
f.write(TEMPLATE.format(title="Новости", content=f"<h2>Последние новости</h2><ul>{news_links}</ul>"))
for md_file in news_files:
build_page(md_file, Path(f'public/news/{md_file.stem}.html'), md_file.stem.replace('-', ' ').title())
# 3. Страница сотрудников (с группировкой и ссылками!)
staff_by_lab = {}
unassigned = []
for md_file in sorted(Path('content/staff').glob('*.md')):
with open(md_file, 'r', encoding='utf-8') as f:
meta, _ = parse_front_matter(f.read())
name = meta.get('name', md_file.stem.replace('-', ' ').title())
degree = meta.get('degree', '')
position = meta.get('position', '')
lab = meta.get('lab', 'Без подразделения')
details = []
if degree: details.append(degree)
if position: details.append(position)
details_str = ", ".join(details)
# Создаем ссылку на персональную страницу
person_html = f'''<div class="person">
<div class="person-name"><a href="staff/{md_file.stem}.html">{name}</a></div>
<div class="person-details">{details_str}</div>
</div>'''
if lab == 'Без подразделения' or not lab:
unassigned.append(person_html)
else:
if lab not in staff_by_lab:
staff_by_lab[lab] = []
staff_by_lab[lab].append(person_html)
# Формируем HTML для страницы сотрудников
staff_html = "<h2>Наши сотрудники</h2>"
lab_order = [
"Лаборатория математики и телекоммуникаций",
"Лаборатория теоретической и вычислительной физики",
"Лаборатория экспериментальной физики"
]
for lab in lab_order:
if lab in staff_by_lab:
staff_html += f"<h3>{lab}</h3>"
for p in staff_by_lab[lab]:
staff_html += p
for lab, people in staff_by_lab.items():
if lab not in lab_order:
staff_html += f"<h3>{lab}</h3>"
for p in people:
staff_html += p
if unassigned:
staff_html += "<h3>Сотрудники (без привязки к лаборатории)</h3>"
for p in unassigned:
staff_html += p
with open('public/staff.html', 'w', encoding='utf-8') as f:
f.write(TEMPLATE.format(title="Сотрудники", content=staff_html))
# 4. Создаем индивидуальные страницы для каждого сотрудника
for md_file in Path('content/staff').glob('*.md'):
with open(md_file, 'r', encoding='utf-8') as f:
meta, body = parse_front_matter(f.read())
name = meta.get('name', md_file.stem.replace('-', ' ').title())
# Добавляем ссылку "Назад к списку" в начало страницы
full_content = f'<a href="/staff.html" class="back-link">← Назад к списку сотрудников</a>\n{body}'
html_content = markdown.markdown(full_content, extensions=['fenced_code', 'tables'])
with open(Path(f'public/staff/{md_file.stem}.html'), 'w', encoding='utf-8') as f:
f.write(TEMPLATE.format(title=name, content=html_content))
# 5. Копирование статических файлов (картинки)
if Path('static').exists():
shutil.rmtree('public/static', ignore_errors=True)
shutil.copytree('static', 'public/static')
print("✅ Статические файлы скопированы")
print("✅ Сайт успешно собран! Сотрудники сгруппированы и кликабельны.")
if __name__ == '__main__':
main()