((* macro expand_function(function_name, function_contents, x = "Function") *))
    \texttt{((( tex_escape(function_name) )))}:

    ((* if function_contents["present"] *))
        \begin{itemize}
            \item Arguments: \pyth{((( function_contents["arguments"] )))}
            \item Documentation: ((( len_documentation(function_contents["documentation"]["comments"], function_contents["documentation"]["doc"]) ))) characters long
            ((* if tex_show_full_docs == "True" *))
                               
                \textbf{Comments:}
                ((*- if function_contents["documentation"]["comments"] == "None" *))
                    \errortext{No comments present.}
                ((* else *))
                    \begin{lstlisting}
((( function_contents["documentation"]["comments"] )))
                    \end{lstlisting}
                ((* endif *))

                \textbf{Docstring}:
                ((*- if function_contents["documentation"]["doc"] == "None" *))
                    \errortext{No docstring present.}
                ((* else *))
                    \begin{lstlisting}
((( function_contents["documentation"]["doc"] )))
                    \end{lstlisting}
                ((* endif *))
            ((* endif *))
            \item Code: ((( get_source_numlines(function_contents["source_code"]) )))
            ((* if tex_show_source == "True" *))
                \begin{python}
((( function_contents["source_code"] )))
                \end{python}
            ((* endif *))
        \end{itemize}
    ((* else *))
        \errortext{((( x ))) \texttt{((( tex_escape(function_name) )))} not present.}
    ((* endif *))
((* endmacro *))

\documentclass{article}

\usepackage{pythonhighlight}

\usepackage[margin=1in]{geometry} % margins
\usepackage{multicol} % columns
\usepackage{float} % layout
\usepackage{forest} % for the class tree
\usepackage{pdfpages} % for importing the test results pdf
\usepackage{xcolor} % colours
\usepackage{listings}
\lstset{
basicstyle=\small\ttfamily,
columns=flexible,
breaklines=true
}

\newcommand{\errortext}[1]{\textcolor{red}{\textbf{#1}}}

\author{((( student_no )))}
\title{((( tex_escape(name) ))) - Automatic marking report}

\begin{document}

((* if tex_columns != "1" *))
\begin{multicols}{((( tex_columns )))}
((* endif *))

\maketitle
\section{Class Tree}

\begin{figure}[H]
    \centering
    ((# \begin{forest}
        ((( recurse_class_tree_forest(class_tree)|indent(8, False) )))
    \end{forest} #))
    \begin{lstlisting}
((( recurse_class_tree_text(class_tree) )))
    \end{lstlisting}
    \caption{Class inheritance tree}
\end{figure}

\section{File Analysis}
((* set flat_files = flatten_struct(files) *))
((* for filename, files_contents in flat_files.items() *))
    \subsection{\texttt{((( tex_escape(filename) )))}}
    ((* if files_contents["present"] *))
        ((* if files_contents["has_exception"] *))
            \errortext{File cannot be run - has compile time exception.}

            Please note that this file cannot be analysed or have tests preformed upon it-
            this can lead to the whole test suite failing if another module imports this.
                    
            \textbf{Exception Type:} \texttt{((( tex_escape(files_contents["exception"]["type"]) )))}
            
            \textbf{Exception String:} \texttt{((( tex_escape(files_contents["exception"]["str"]) )))}

            \textbf{Full Traceback:}

            \begin{lstlisting}
((( files_contents["exception"]["traceback"] )))
            \end{lstlisting}
        ((* else *))
            \begin{itemize}
            \item \textbf{Documentation:}

            ((( len_documentation(files_contents["documentation"]["comments"], files_contents["documentation"]["doc"]) ))) characters long
            ((* if tex_show_full_docs == "True" *))
                
                \item \textbf{Comments:}
                ((*- if files_contents["documentation"]["comments"] == "None" *))
                    \errortext{No comments present.}
                ((* else *))
                    \begin{lstlisting}
((( files_contents["documentation"]["comments"] )))
                    \end{lstlisting}
                ((* endif *))

                \item \textbf{Docstring:}
                ((*- if files_contents["documentation"]["doc"] == "None" *))
                    \errortext{No docstring present.}
                ((* else *))
                    \begin{lstlisting}
((( files_contents["documentation"]["doc"] )))
                    \end{lstlisting}
                ((* endif *))

            ((* endif *))
            \end{itemize}

            ((* if "classes" in files_contents.keys() *))
                \subsubsection{Classes}

                ((* set flat_classes = flatten_struct(files_contents["classes"]) *))
                ((* for class_name, class_contents in flat_classes.items() *))
                \begin{itemize}
                
                    
                    \item \texttt{((( class_name )))}:

                    ((* if class_contents["present"] *))
                        \begin{itemize}
                        \item \textbf{Documentation:}
                        ((( len_documentation(class_contents["documentation"]["comments"], class_contents["documentation"]["doc"]) ))) characters long
                        
                        ((* if tex_show_full_docs == "True" *))
                        
                        
                        \item \textbf{Comments:}

                        ((* if class_contents["documentation"]["comments"] == "None" -*))
                            \errortext{No comments present.}
                        ((* else *))
                            \begin{lstlisting}
((( class_contents["documentation"]["comments"] )))
                            \end{lstlisting}
                        ((* endif *))

                        
                        \item \textbf{Docstring:}

                        ((* if class_contents["documentation"]["doc"] == "None" -*))
                            \errortext{No docstring present.}
                        ((* else *))
                            \begin{lstlisting}
((( class_contents["documentation"]["doc"] )))
                            \end{lstlisting}
                        ((* endif *))

                        ((* if "methods" in class_contents.keys() *))
                        \item \textbf{Methods:}
                            ((* set flat_methods = flatten_struct(class_contents["methods"]) *))
                            \begin{itemize}
                            ((* for method_name, method_contents in flat_methods.items() *))
                                \item ((( expand_function(method_name, method_contents, x = "Method") )))
                            ((* endfor *))
                            \end{itemize}

                        ((* endif *))
                    \end{itemize}
                    ((* endif *))
                    
                    ((* else *))
                        
                        \errortext{Class not present.}

                    ((* endif *))

                \end{itemize}
                ((* endfor *))


            ((* endif *))

            ((* if "functions" in files_contents.keys() *))
                \subsubsection{Functions}
                ((* set flat_functions = flatten_struct(files_contents["functions"]) *))
                \begin{itemize}
                ((* for function_name, function_contents in flat_functions.items() *))
                    \item ((( expand_function(function_name, function_contents) )))
                ((* endfor *))
                \end{itemize}
            ((* endif *))

            \subsubsection{Runtime Analysis}
            ((* set flat_runtime = flatten_struct(files_contents["run"]) *))
            ((* if len_(flat_runtime) > 0 *))
            \begin{itemize}
            ((* for cmd, runtime_contents in flat_runtime.items() *))
                \item Command: \texttt{((( tex_escape(cmd) )))}
                \item Monitor:
                ((*- if "monitor" in runtime_contents.keys() *))
                    \texttt{((( tex_escape(runtime_contents["monitor"]) )))}
                ((*- else *))
                    stdout
                ((*- endif *))
                \item Regexes:
                ((* for regex_, results in runtime_contents["regexes"].items() *))
                \begin{itemize}
                    \item \texttt{((( tex_escape(regex_) )))}:
                    \begin{itemize}
                        \item Found occurrences: ((( len_(results) )))
                        ((* if txt_show_all_regex_occurrences == "True" and len_(results) > 0 *))
                        \item Occurences list:
                        \begin{enumerate}
                        ((* for result in results *))
                            \item \texttt{((( tex_escape(result.replace("\n", "\\n")) )))}
                        ((* endfor *))
                        \end{enumerate}
                        ((* endif *))
                    \end{itemize}
                \end{itemize}
                ((*- endfor -*))
            ((* endfor *))
            \end{itemize}
            ((* endif *))

        ((* endif *))
    ((* else *))
        \errortext{File is not present.}
    ((* endif *))
((* endfor *))

\section{Tests}
((* if test_results["pytest_report"] == "*** No Tests ***" *))
    No tests were executed.
((* else *))
    \includepdf[pages={1-},scale=1.0]{((( junit_xml_to_html(test_results["junitxml"], student_no) )))}
((* endif *))

((* if tex_columns != "1" *))
\end{multicols}
((* endif *))

\end{document}