172 lines
7.8 KiB
Python
172 lines
7.8 KiB
Python
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() |