diff --git a/README.md b/README.md index 4e99465..4514b73 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ To export given solution use: --output_dir output_dir ``` +## Converting notebooks to PDF files + +To export notebooks to PDF files use: + +```bash +./convert_notebooks.py --notebooks_dir ./notebooks +``` + ## Implemented Algorithms ### Matrix multiplication diff --git a/convert_notebooks.py b/convert_notebooks.py new file mode 100644 index 0000000..3e96c5d --- /dev/null +++ b/convert_notebooks.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from pathlib import Path + +import nbformat +from nbconvert import HTMLExporter +from weasyprint import HTML + + +def ipynb_to_pdf(ipynb_path: Path, pdf_path: Path): + try: + with open(ipynb_path, encoding="utf-8") as f: + nb = nbformat.read(f, as_version=4) + + html_exporter = HTMLExporter() + html_exporter.exclude_input_prompt = True + html_exporter.exclude_output_prompt = True + body, _ = html_exporter.from_notebook_node(nb) + + HTML(string=body, base_url=str(ipynb_path.parent)).write_pdf(str(pdf_path)) + print(f"Converted: {ipynb_path.name} -> {pdf_path.name}") + except Exception as e: + print(f"Failed to convert {ipynb_path.name} to pdf: {e}") + + +def sync_notebooks_to_pdf(directory_path: str, output_path: str): + base_path = Path(directory_path) + + if output_path: + out_base = Path(output_path) + else: + out_base = base_path / "resources" + + notebooks = list(base_path.glob("*.ipynb")) + + if not notebooks: + print(f"No notebooks found in: {directory_path}") + return + + for ipynb_path in notebooks: + pdf_path = out_base / ipynb_path.with_suffix(".pdf").name + + should_convert = False + + if not pdf_path.exists(): + should_convert = True + else: + ipynb_mtime = ipynb_path.stat().st_mtime + pdf_mtime = pdf_path.stat().st_mtime + + if ipynb_mtime > pdf_mtime: + should_convert = True + + if should_convert: + ipynb_to_pdf(ipynb_path, pdf_path) + else: + print(f"Skipped: {ipynb_path.name} (PDF is up to date)") + + +def main(): + parser = argparse.ArgumentParser(description="Sync jupyter notebooks to PDF") + + parser.add_argument( + "--notebooks_dir", + "-d", + required=True, + help="Path to directory with notebooks", + ) + + parser.add_argument( + "--output_dir", + "-o", + required=False, + default=None, + help="Output directory for PDF files", + ) + + args = parser.parse_args() + + sync_notebooks_to_pdf(args.notebooks_dir, args.output_dir) + + +if __name__ == "__main__": + main() diff --git a/generate_report.py b/generate_report.py index c48e260..7336337 100755 --- a/generate_report.py +++ b/generate_report.py @@ -7,6 +7,8 @@ from pathlib import Path import markdown # type: ignore +import nbformat +from nbconvert import HTMLExporter from weasyprint import HTML @@ -22,6 +24,19 @@ def md_to_pdf(md_path: Path, pdf_path: Path): HTML(string=html, base_url=md_path.parent).write_pdf(str(pdf_path)) +def ipynb_to_pdf(ipynb_path: Path, pdf_path: Path): + """Convert Jupyter notebook to PDF via HTML""" + with open(ipynb_path, encoding="utf-8") as f: + nb = nbformat.read(f, as_version=4) + + html_exporter = HTMLExporter() + html_exporter.exclude_input_prompt = True + html_exporter.exclude_output_prompt = True + body, _ = html_exporter.from_notebook_node(nb) + + HTML(string=body, base_url=ipynb_path.parent).write_pdf(str(pdf_path)) + + def resolve_module_path(module_name): """Resolve module path using importlib""" spec = importlib.util.find_spec(module_name) @@ -97,22 +112,30 @@ def export_code(entry_file: Path, output_txt: Path): def main(): parser = argparse.ArgumentParser(description="Generate project report.") parser.add_argument("--description_file", "-d", required=True) - parser.add_argument("--code", "-c", required=True) + parser.add_argument("--code", "-c", required=False, default=None) parser.add_argument("--output_dir", "-o", default="report") args = parser.parse_args() - desc_path = Path(args.description_file) - code_path = Path(args.code) output_dir = Path(args.output_dir) - output_dir.mkdir(parents=True, exist_ok=True) pdf_output = output_dir / "report.pdf" txt_output = output_dir / "code.txt" - md_to_pdf(desc_path, pdf_output) - export_code(code_path, txt_output) + desc_path = Path(args.description_file) + + if desc_path.suffix == ".md": + md_to_pdf(desc_path, pdf_output) + elif desc_path.suffix == ".ipynb": + ipynb_to_pdf(desc_path, pdf_output) + else: + raise ValueError(f"Unsupported file type: {desc_path.suffix}") + + if args.code is not None: + code_path = Path(args.code) + + export_code(code_path, txt_output) if __name__ == "__main__": diff --git a/notebooks/matrix_norms.ipynb b/notebooks/matrix_norms.ipynb new file mode 100644 index 0000000..64e8bad --- /dev/null +++ b/notebooks/matrix_norms.ipynb @@ -0,0 +1,53 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a65bf3b0", + "metadata": {}, + "source": [ + "# Normy macierzowe\n", + "\n", + "### **Autorzy: Maja Byrecka, MichaƂ Kowalczyk**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72cbd32f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-2.0\n" + ] + } + ], + "source": [ + "from matrix_calculus.matrix_norms import first_norm, second_norm, p_norm, inf_norm" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/resources/matrix_norms.pdf b/notebooks/resources/matrix_norms.pdf new file mode 100644 index 0000000..1187b1e Binary files /dev/null and b/notebooks/resources/matrix_norms.pdf differ diff --git a/pyproject.toml b/pyproject.toml index f6d3e78..6946fe3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "matrix_calculus" version = "1.5.0" description = "Matrix calculus toolkit" requires-python = ">=3.10" -dependencies = ["numpy", "seaborn", "matplotlib", "weasyprint", "markdown"] +dependencies = ["numpy", "seaborn", "matplotlib", "weasyprint", "markdown", "notebook", "nbconvert"] [tool.setuptools.packages.find] where = ["."]