Module src.PyOghma_ML.Latex

This module provides a class for programmatically generating LaTeX documents.

The Document class simplifies the creation of LaTeX files by providing methods to define document structure, add sections, figures, tables, and compile the document into a PDF. It is designed to automate the process of generating reports and visualizations in a LaTeX-compatible format.

Classes

class Document (document_class: str,
document_properties: List[str],
packages: List[str],
**kwargs: Any)
Expand source code
class Document:
    """
    A class for generating LaTeX documents programmatically.

    This class provides methods for creating LaTeX documents, adding sections,
    figures, tables, and compiling the document into a PDF.
    """

    def __init__(self, document_class: str, document_properties: List[str], packages: List[str], **kwargs: Any) -> None:
        """
        Initialize a Document instance.

        Args:
            document_class (str): The LaTeX document class (e.g., 'article').
            document_properties (list): List of properties for the document class (e.g., ['12pt']).
            packages (list): List of LaTeX packages to include (e.g., ['graphicx']).
            **kwargs: Additional keyword arguments.
        """
        self.file = io.StringIO()
        self.tab = 0
        self.newline = True

        self.document_class(document_class, document_properties)
        self.usepakage(packages)
        self.file_header()
        self.write(self.header)

    def document_class(self, document_class: str, document_properties: List[str]) -> None:
        """
        Define the LaTeX document class and its properties.

        Args:
            document_class (str): The LaTeX document class.
            document_properties (list): List of properties for the document class.
        """
        options = ["[" + str(dp) + "]" for dp in document_properties]
        options = ''.join(options)
        self.dc = '\\documentclass' + options + '{' + document_class + '}\n'

    def usepakage(self, packages: List[str]) -> None:
        """
        Include LaTeX packages in the document.

        Args:
            packages (list): List of LaTeX packages to include.
        """
        up = ['\\usepackage{' + str(p) + '}\n' for p in packages]
        self.up = ''.join(up)

    def geometry(self, left: str = '2cm', right: str = '2cm', top: str = '1cm', bottom: str = '2cm', paper: str = 'a4paper') -> None:
        """
        Set the page geometry for the document.

        Args:
            left (str): Left margin (default: '2cm').
            right (str): Right margin (default: '2cm').
            top (str): Top margin (default: '1cm').
            bottom (str): Bottom margin (default: '2cm').
            paper (str): Paper size (default: 'a4paper').
        """
        x = 'geometry'
        y = 'left=' + str(left) + ',right=' + str(right) + ',top=' + str(top) + ',bottom=' + str(
            bottom) + ',paper=' + str(paper)
        self.write(self.command(x, y))

    def file_header(self) -> None:
        """
        Generate the LaTeX file header, including the document class and packages.
        """
        header = ''.join([self.dc, self.up])
        self.header = header

    def write(self, x: str) -> None:
        """
        Write content to the LaTeX document.

        Args:
            x (str): The content to write.
        """
        tabs = '\t' * self.tab
        self.file.write(tabs+x)
        if self.newline:
            self.file.write('\n')

    def save_tex(self) -> None:
        """
        Save the LaTeX document to a temporary .tex file.

        This method writes the accumulated LaTeX content to a file named
        'tempfile.tex' in the current working directory. The file can then
        be compiled to PDF or edited manually.

        Note:
            The file is saved as 'tempfile.tex' and will overwrite any
            existing file with the same name.
        """
        temp_tex = 'tempfile.tex'
        f = open(temp_tex, 'w')
        f.write(self.file.getvalue())
        f.close()

    def compile(self) -> None:
        """
        Compile the LaTeX document into a PDF using pdflatex.

        This method executes the pdflatex command to compile the temporary
        LaTeX file into a PDF document. The compilation output is suppressed
        to avoid cluttering the console.

        Note:
            Requires pdflatex to be installed and available in the system PATH.
            The compiled PDF will be named 'tempfile.pdf' in the working directory.

        Raises:
            OSError: If pdflatex is not found or compilation fails.
        """
        os.system("pdflatex tempfile.tex #> /dev/null 2>&1")

    def title(self, title: str) -> None:
        """
        Set the title of the document.

        Args:
            title (str): The title text.
        """
        self.write('\\title{'+title+'}\n')

    def date(self, date: str = '\\today') -> None:
        """
        Set the date of the document.

        Args:
            date (str): The date text (default: '\\today').
        """
        self.write('\\date{'+date+'}\n')

    def author(self, author: str = 'T.H. Parry-Williams') -> None:
        """
        Set the author of the document.

        Args:
            author (str): The author name (default: 'T.H. Parry-Williams').
        """
        self.write('\\author{'+author+'}\n')

    def begin_document(self) -> None:
        """
        Begin the LaTeX document environment.
        """
        self.write('\\begin{document}\n')

    def end_document(self) -> None:
        """
        End the LaTeX document environment.
        """
        self.write('\\end{document}\n')

    def make_title(self) -> None:
        """
        Add the title, author, and date to the document.
        """
        self.write(self.command('maketitle'))

    def section(self, x: str) -> None:
        """
        Add a section to the document.

        Args:
            x (str): The section title.
        """
        self.write('\\section{'+str(x)+'}\n')

    def subsection(self, x: str) -> None:
        """
        Add a subsection to the document.

        Args:
            x (str): The subsection title.
        """
        self.write('\\subsection{'+str(x)+'}\n')

    def subsubsection(self, x: str) -> None:
        """
        Add a subsubsection to the document.

        Args:
            x (str): The subsubsection title.
        """
        self.write('\\subsubsection{'+str(x)+'}\n')

    def begin(self, x: str, options: Optional[List[str]] = None, options1: Optional[List[str]] = None) -> None:
        """
        Begin a LaTeX environment.

        Args:
            x (str): The environment name.
            options (list, optional): List of options for the environment.
            options1 (list, optional): Additional options for the environment.
        """
        if options is not None:
            option = ['['+o+']' for o in options]
            option = ''.join(option)

        if options1 is not None:
            option1 = '{' + ''.join(options1) + '}'

        if options is not None:
            if options1 is not None:
                self.write('\\begin{'+str(x)+'}'+option+option1+'\n')
            else:
                self.write('\\begin{'+str(x)+'}'+option+'\n')
        else:
            if options1 is not None:
                self.write('\\begin{'+str(x)+'}'+option1+'\n')
            else:
                self.write('\\begin{'+str(x)+'}\n')

    def end(self, x: str) -> None:
        """
        End a LaTeX environment.

        Args:
            x (str): The environment name.
        """
        self.write('\\end{'+str(x)+'}\n')

    def newpage(self) -> None:
        """
        Add a new page to the document.
        """
        self.write(self.command('newpage'))

    @staticmethod
    def command(x: str, y: Optional[str] = None) -> str:
        """
        Generate a LaTeX command.

        Args:
            x (str): The command name.
            y (str, optional): The command argument.

        Returns:
            str: The formatted LaTeX command.
        """
        if y is None:
            x = '\\' + x + '\n'
        else:
            x = '\\' + x + '{' + y + '}\n'
        return x

    def includegraphics(self, x: str, width: str, newline: bool = True) -> None:
        """
        Include a graphic in the document.

        Args:
            x (str): The path to the graphic file.
            width (str): The width of the graphic.
            newline (bool): Whether to add a newline after the graphic (default: True).
        """
        command = '\\includegraphics'
        w = '[width=' + str(width) + ']'
        if newline:
            command = command + w + '{' + str(x) + '}\n'
        else:
            command = command + w + '{' + str(x) + '}'
        self.write(command)

    def figure(self, arg: str, centering: bool = True, position: str = 'h', width: str = '\\textwidth', caption: str = '') -> None:
        """
        Add a figure to the document.

        Args:
            arg (str): The path to the figure file.
            centering (bool): Whether to center the figure (default: True).
            position (str): The position specifier (default: 'h').
            width (str): The width of the figure (default: '\\textwidth').
            caption (str): The caption for the figure (default: '').
        """
        self.begin('figure', options=[position])
        self.tab = 1

        if centering:
            self.write(self.command('centering'))

        self.includegraphics(arg, width=width, newline=False)

        if caption != '':
            self.write(self.command('caption', caption))

        self.tab = 0
        self.end('figure')

    def subfigure(self, *args: str, centering: bool = True, position: str = 'h', caption: str = '', width: float = 0.5) -> None:
        """
        Add subfigures to the document.

        Args:
            *args: Paths to the subfigure files.
            centering (bool): Whether to center the subfigures (default: True).
            position (str): The position specifier (default: 'h').
            caption (str): The caption for the subfigures (default: '').
            width (float): The width of each subfigure as a fraction of the text width (default: 0.5).
        """
        self.newline = False
        self.begin('figure', options=['H'])
        self.tab = 1
        self.write(self.command('centering'))
        for i in range(len(args)):
            self.tab = 1
            self.begin('subfigure', options1=[str(width) + '\\textwidth'])
            self.tab = 2
            if centering:
                self.write(self.command('centering'))
                self.includegraphics(args[i], width='1\\linewidth', newline=True)
                self.newline = False
            self.tab = 1
            self.end('subfigure')
            self.write(self.command('hfill'))
            self.tab = 0
        if caption != '':
            self.write(self.command('caption', caption))
        self.end('figure')
        self.newline = True

    def table(self, df: Any, centering: bool = True, position: str = 'H', caption: str = '', highlight: bool = False) -> None:
        """
        Add a table to the document.

        Args:
            df (pandas.DataFrame): The data to include in the table.
            centering (bool): Whether to center the table (default: True).
            position (str): The position specifier (default: 'H').
            caption (str): The caption for the table (default: '').
            highlight (bool): Whether to highlight specific cells (default: False).
        """
        col_num = len(df.columns)
        col_format = 'l' + 'c' * (col_num)
        if highlight:
            df = df.style.background_gradient(cmap='RdYlGn_r', subset='MAPE (\%)', vmin=0, vmax=100).hide(axis="index")
        else:
            df = df.style.hide(axis="index")
        if caption != '':
            self.write(
                df.to_latex(column_format=col_format, caption=caption, position=position, position_float='centering',
                            hrules=True, convert_css=True))
        else:
            self.write(df.to_latex(column_format=col_format, position=position, position_float='centering', hrules=True,
                                   convert_css=True))

A class for generating LaTeX documents programmatically.

This class provides methods for creating LaTeX documents, adding sections, figures, tables, and compiling the document into a PDF.

Initialize a Document instance.

Args

document_class : str
The LaTeX document class (e.g., 'article').
document_properties : list
List of properties for the document class (e.g., ['12pt']).
packages : list
List of LaTeX packages to include (e.g., ['graphicx']).
**kwargs
Additional keyword arguments.

Static methods

def command(x: str, y: str | None = None) ‑> str
Expand source code
@staticmethod
def command(x: str, y: Optional[str] = None) -> str:
    """
    Generate a LaTeX command.

    Args:
        x (str): The command name.
        y (str, optional): The command argument.

    Returns:
        str: The formatted LaTeX command.
    """
    if y is None:
        x = '\\' + x + '\n'
    else:
        x = '\\' + x + '{' + y + '}\n'
    return x

Generate a LaTeX command.

Args

x : str
The command name.
y : str, optional
The command argument.

Returns

str
The formatted LaTeX command.

Methods

def author(self, author: str = 'T.H. Parry-Williams') ‑> None
Expand source code
def author(self, author: str = 'T.H. Parry-Williams') -> None:
    """
    Set the author of the document.

    Args:
        author (str): The author name (default: 'T.H. Parry-Williams').
    """
    self.write('\\author{'+author+'}\n')

Set the author of the document.

Args

author : str
The author name (default: 'T.H. Parry-Williams').
def begin(self, x: str, options: List[str] | None = None, options1: List[str] | None = None) ‑> None
Expand source code
def begin(self, x: str, options: Optional[List[str]] = None, options1: Optional[List[str]] = None) -> None:
    """
    Begin a LaTeX environment.

    Args:
        x (str): The environment name.
        options (list, optional): List of options for the environment.
        options1 (list, optional): Additional options for the environment.
    """
    if options is not None:
        option = ['['+o+']' for o in options]
        option = ''.join(option)

    if options1 is not None:
        option1 = '{' + ''.join(options1) + '}'

    if options is not None:
        if options1 is not None:
            self.write('\\begin{'+str(x)+'}'+option+option1+'\n')
        else:
            self.write('\\begin{'+str(x)+'}'+option+'\n')
    else:
        if options1 is not None:
            self.write('\\begin{'+str(x)+'}'+option1+'\n')
        else:
            self.write('\\begin{'+str(x)+'}\n')

Begin a LaTeX environment.

Args

x : str
The environment name.
options : list, optional
List of options for the environment.
options1 : list, optional
Additional options for the environment.
def begin_document(self) ‑> None
Expand source code
def begin_document(self) -> None:
    """
    Begin the LaTeX document environment.
    """
    self.write('\\begin{document}\n')

Begin the LaTeX document environment.

def compile(self) ‑> None
Expand source code
def compile(self) -> None:
    """
    Compile the LaTeX document into a PDF using pdflatex.

    This method executes the pdflatex command to compile the temporary
    LaTeX file into a PDF document. The compilation output is suppressed
    to avoid cluttering the console.

    Note:
        Requires pdflatex to be installed and available in the system PATH.
        The compiled PDF will be named 'tempfile.pdf' in the working directory.

    Raises:
        OSError: If pdflatex is not found or compilation fails.
    """
    os.system("pdflatex tempfile.tex #> /dev/null 2>&1")

Compile the LaTeX document into a PDF using pdflatex.

This method executes the pdflatex command to compile the temporary LaTeX file into a PDF document. The compilation output is suppressed to avoid cluttering the console.

Note

Requires pdflatex to be installed and available in the system PATH. The compiled PDF will be named 'tempfile.pdf' in the working directory.

Raises

OSError
If pdflatex is not found or compilation fails.
def date(self, date: str = '\\today') ‑> None
Expand source code
def date(self, date: str = '\\today') -> None:
    """
    Set the date of the document.

    Args:
        date (str): The date text (default: '\\today').
    """
    self.write('\\date{'+date+'}\n')

Set the date of the document.

Args

date : str
The date text (default: '\today').
def document_class(self, document_class: str, document_properties: List[str]) ‑> None
Expand source code
def document_class(self, document_class: str, document_properties: List[str]) -> None:
    """
    Define the LaTeX document class and its properties.

    Args:
        document_class (str): The LaTeX document class.
        document_properties (list): List of properties for the document class.
    """
    options = ["[" + str(dp) + "]" for dp in document_properties]
    options = ''.join(options)
    self.dc = '\\documentclass' + options + '{' + document_class + '}\n'

Define the LaTeX document class and its properties.

Args

document_class : str
The LaTeX document class.
document_properties : list
List of properties for the document class.
def end(self, x: str) ‑> None
Expand source code
def end(self, x: str) -> None:
    """
    End a LaTeX environment.

    Args:
        x (str): The environment name.
    """
    self.write('\\end{'+str(x)+'}\n')

End a LaTeX environment.

Args

x : str
The environment name.
def end_document(self) ‑> None
Expand source code
def end_document(self) -> None:
    """
    End the LaTeX document environment.
    """
    self.write('\\end{document}\n')

End the LaTeX document environment.

def figure(self,
arg: str,
centering: bool = True,
position: str = 'h',
width: str = '\\textwidth',
caption: str = '') ‑> None
Expand source code
def figure(self, arg: str, centering: bool = True, position: str = 'h', width: str = '\\textwidth', caption: str = '') -> None:
    """
    Add a figure to the document.

    Args:
        arg (str): The path to the figure file.
        centering (bool): Whether to center the figure (default: True).
        position (str): The position specifier (default: 'h').
        width (str): The width of the figure (default: '\\textwidth').
        caption (str): The caption for the figure (default: '').
    """
    self.begin('figure', options=[position])
    self.tab = 1

    if centering:
        self.write(self.command('centering'))

    self.includegraphics(arg, width=width, newline=False)

    if caption != '':
        self.write(self.command('caption', caption))

    self.tab = 0
    self.end('figure')

Add a figure to the document.

Args

arg : str
The path to the figure file.
centering : bool
Whether to center the figure (default: True).
position : str
The position specifier (default: 'h').
width : str
The width of the figure (default: '\textwidth').
caption : str
The caption for the figure (default: '').
def file_header(self) ‑> None
Expand source code
def file_header(self) -> None:
    """
    Generate the LaTeX file header, including the document class and packages.
    """
    header = ''.join([self.dc, self.up])
    self.header = header

Generate the LaTeX file header, including the document class and packages.

def geometry(self,
left: str = '2cm',
right: str = '2cm',
top: str = '1cm',
bottom: str = '2cm',
paper: str = 'a4paper') ‑> None
Expand source code
def geometry(self, left: str = '2cm', right: str = '2cm', top: str = '1cm', bottom: str = '2cm', paper: str = 'a4paper') -> None:
    """
    Set the page geometry for the document.

    Args:
        left (str): Left margin (default: '2cm').
        right (str): Right margin (default: '2cm').
        top (str): Top margin (default: '1cm').
        bottom (str): Bottom margin (default: '2cm').
        paper (str): Paper size (default: 'a4paper').
    """
    x = 'geometry'
    y = 'left=' + str(left) + ',right=' + str(right) + ',top=' + str(top) + ',bottom=' + str(
        bottom) + ',paper=' + str(paper)
    self.write(self.command(x, y))

Set the page geometry for the document.

Args

left : str
Left margin (default: '2cm').
right : str
Right margin (default: '2cm').
top : str
Top margin (default: '1cm').
bottom : str
Bottom margin (default: '2cm').
paper : str
Paper size (default: 'a4paper').
def includegraphics(self, x: str, width: str, newline: bool = True) ‑> None
Expand source code
def includegraphics(self, x: str, width: str, newline: bool = True) -> None:
    """
    Include a graphic in the document.

    Args:
        x (str): The path to the graphic file.
        width (str): The width of the graphic.
        newline (bool): Whether to add a newline after the graphic (default: True).
    """
    command = '\\includegraphics'
    w = '[width=' + str(width) + ']'
    if newline:
        command = command + w + '{' + str(x) + '}\n'
    else:
        command = command + w + '{' + str(x) + '}'
    self.write(command)

Include a graphic in the document.

Args

x : str
The path to the graphic file.
width : str
The width of the graphic.
newline : bool
Whether to add a newline after the graphic (default: True).
def make_title(self) ‑> None
Expand source code
def make_title(self) -> None:
    """
    Add the title, author, and date to the document.
    """
    self.write(self.command('maketitle'))

Add the title, author, and date to the document.

def newpage(self) ‑> None
Expand source code
def newpage(self) -> None:
    """
    Add a new page to the document.
    """
    self.write(self.command('newpage'))

Add a new page to the document.

def save_tex(self) ‑> None
Expand source code
def save_tex(self) -> None:
    """
    Save the LaTeX document to a temporary .tex file.

    This method writes the accumulated LaTeX content to a file named
    'tempfile.tex' in the current working directory. The file can then
    be compiled to PDF or edited manually.

    Note:
        The file is saved as 'tempfile.tex' and will overwrite any
        existing file with the same name.
    """
    temp_tex = 'tempfile.tex'
    f = open(temp_tex, 'w')
    f.write(self.file.getvalue())
    f.close()

Save the LaTeX document to a temporary .tex file.

This method writes the accumulated LaTeX content to a file named 'tempfile.tex' in the current working directory. The file can then be compiled to PDF or edited manually.

Note

The file is saved as 'tempfile.tex' and will overwrite any existing file with the same name.

def section(self, x: str) ‑> None
Expand source code
def section(self, x: str) -> None:
    """
    Add a section to the document.

    Args:
        x (str): The section title.
    """
    self.write('\\section{'+str(x)+'}\n')

Add a section to the document.

Args

x : str
The section title.
def subfigure(self,
*args: str,
centering: bool = True,
position: str = 'h',
caption: str = '',
width: float = 0.5) ‑> None
Expand source code
def subfigure(self, *args: str, centering: bool = True, position: str = 'h', caption: str = '', width: float = 0.5) -> None:
    """
    Add subfigures to the document.

    Args:
        *args: Paths to the subfigure files.
        centering (bool): Whether to center the subfigures (default: True).
        position (str): The position specifier (default: 'h').
        caption (str): The caption for the subfigures (default: '').
        width (float): The width of each subfigure as a fraction of the text width (default: 0.5).
    """
    self.newline = False
    self.begin('figure', options=['H'])
    self.tab = 1
    self.write(self.command('centering'))
    for i in range(len(args)):
        self.tab = 1
        self.begin('subfigure', options1=[str(width) + '\\textwidth'])
        self.tab = 2
        if centering:
            self.write(self.command('centering'))
            self.includegraphics(args[i], width='1\\linewidth', newline=True)
            self.newline = False
        self.tab = 1
        self.end('subfigure')
        self.write(self.command('hfill'))
        self.tab = 0
    if caption != '':
        self.write(self.command('caption', caption))
    self.end('figure')
    self.newline = True

Add subfigures to the document.

Args

*args
Paths to the subfigure files.
centering : bool
Whether to center the subfigures (default: True).
position : str
The position specifier (default: 'h').
caption : str
The caption for the subfigures (default: '').
width : float
The width of each subfigure as a fraction of the text width (default: 0.5).
def subsection(self, x: str) ‑> None
Expand source code
def subsection(self, x: str) -> None:
    """
    Add a subsection to the document.

    Args:
        x (str): The subsection title.
    """
    self.write('\\subsection{'+str(x)+'}\n')

Add a subsection to the document.

Args

x : str
The subsection title.
def subsubsection(self, x: str) ‑> None
Expand source code
def subsubsection(self, x: str) -> None:
    """
    Add a subsubsection to the document.

    Args:
        x (str): The subsubsection title.
    """
    self.write('\\subsubsection{'+str(x)+'}\n')

Add a subsubsection to the document.

Args

x : str
The subsubsection title.
def table(self,
df: Any,
centering: bool = True,
position: str = 'H',
caption: str = '',
highlight: bool = False) ‑> None
Expand source code
def table(self, df: Any, centering: bool = True, position: str = 'H', caption: str = '', highlight: bool = False) -> None:
    """
    Add a table to the document.

    Args:
        df (pandas.DataFrame): The data to include in the table.
        centering (bool): Whether to center the table (default: True).
        position (str): The position specifier (default: 'H').
        caption (str): The caption for the table (default: '').
        highlight (bool): Whether to highlight specific cells (default: False).
    """
    col_num = len(df.columns)
    col_format = 'l' + 'c' * (col_num)
    if highlight:
        df = df.style.background_gradient(cmap='RdYlGn_r', subset='MAPE (\%)', vmin=0, vmax=100).hide(axis="index")
    else:
        df = df.style.hide(axis="index")
    if caption != '':
        self.write(
            df.to_latex(column_format=col_format, caption=caption, position=position, position_float='centering',
                        hrules=True, convert_css=True))
    else:
        self.write(df.to_latex(column_format=col_format, position=position, position_float='centering', hrules=True,
                               convert_css=True))

Add a table to the document.

Args

df : pandas.DataFrame
The data to include in the table.
centering : bool
Whether to center the table (default: True).
position : str
The position specifier (default: 'H').
caption : str
The caption for the table (default: '').
highlight : bool
Whether to highlight specific cells (default: False).
def title(self, title: str) ‑> None
Expand source code
def title(self, title: str) -> None:
    """
    Set the title of the document.

    Args:
        title (str): The title text.
    """
    self.write('\\title{'+title+'}\n')

Set the title of the document.

Args

title : str
The title text.
def usepakage(self, packages: List[str]) ‑> None
Expand source code
def usepakage(self, packages: List[str]) -> None:
    """
    Include LaTeX packages in the document.

    Args:
        packages (list): List of LaTeX packages to include.
    """
    up = ['\\usepackage{' + str(p) + '}\n' for p in packages]
    self.up = ''.join(up)

Include LaTeX packages in the document.

Args

packages : list
List of LaTeX packages to include.
def write(self, x: str) ‑> None
Expand source code
def write(self, x: str) -> None:
    """
    Write content to the LaTeX document.

    Args:
        x (str): The content to write.
    """
    tabs = '\t' * self.tab
    self.file.write(tabs+x)
    if self.newline:
        self.file.write('\n')

Write content to the LaTeX document.

Args

x : str
The content to write.