diff --git a/banking_breakdown/__main__.py b/banking_breakdown/__main__.py index 225154e..cdd8a89 100644 --- a/banking_breakdown/__main__.py +++ b/banking_breakdown/__main__.py @@ -1,12 +1,35 @@ import pandas as pd -from banking_breakdown import latex_generator +from banking_breakdown import document_builder +import subprocess +import os +import shutil + +import pandas as pd + +from banking_breakdown import types +import numpy as np def main(): - src = latex_generator.generate_latex() - latex_generator.build_document(src) - # df = pd.read_csv('res/statement.csv', delimiter=';') - # print(df['Betrag']) + categories = ["A", "B", "C", "D", "E", "F", "G"] + values = np.array([10, 12, 53, 12, 90, 23, 32]) + values = values / values.sum() * 100 + + total_value = np.random.normal(size=10) + 4 + net_income = np.diff(total_value) + + category_overview_df = pd.DataFrame( + {"category": categories, "value": values.astype('int32')}) + t = np.linspace(0, total_value.size, total_value.size) + total_value_df = pd.DataFrame({"t": t, "value": total_value}) + t = np.linspace(0, net_income.size, net_income.size) + net_income_df = pd.DataFrame({"t": t, "value": net_income}) + + report_data = types.ReportData(category_overview=category_overview_df, + net_income=net_income_df, + total_value=total_value_df) + + document_builder.build_document(report_data) if __name__ == "__main__": diff --git a/banking_breakdown/document_builder.py b/banking_breakdown/document_builder.py new file mode 100644 index 0000000..23ed8fd --- /dev/null +++ b/banking_breakdown/document_builder.py @@ -0,0 +1,27 @@ +import subprocess +import os +import shutil +from banking_breakdown import types + + +def build_document(report_data: types.ReportData): + # Copy files from 'res/' folder + os.makedirs(os.path.dirname("build/report.tex"), exist_ok=True) + shutil.copyfile("res/report.tex", "build/report.tex") + shutil.copyfile("res/.latexmkrc", "build/.latexmkrc") + + # Write data as csv files + report_data.net_income.to_csv('build/net_income.csv', index=False) + report_data.category_overview.to_csv('build/category_overview.csv', + index=False) + report_data.total_value.to_csv('build/total_value.csv', index=False) + + # Build dockerfile and compile document + subprocess.call("docker build . -f res/Dockerfile.alpine" + " -t banking-breakdown", + shell=True) + subprocess.call("docker run --rm -u `id -u`:`id -g`" + " -v $(realpath .):/project" + " -w /project/build banking-breakdown" + " latexmk -pdf report.tex", + shell=True) diff --git a/banking_breakdown/latex_generator.py b/banking_breakdown/latex_generator.py deleted file mode 100644 index 0f7aedb..0000000 --- a/banking_breakdown/latex_generator.py +++ /dev/null @@ -1,70 +0,0 @@ -import subprocess -import textwrap -import os -import shutil -import docker - - -def generate_latex() -> str: - result = textwrap.dedent("""\ - \\documentclass{article} - - % Packages necessary for common.tex - \\usepackage{amsmath} - \\usepackage{pgfplots} - \\pgfplotsset{compat=newest} - - % Other packages - \\usepackage{float} - \\usepackage{subcaption} - \\usepackage[a4paper, total={6.5in, 9in}]{geometry} - \\usetikzlibrary{positioning} - \\usepackage{ifthen} - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%% Set common options %%%%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - - \\input{../lib/latex-common/common.tex} - \\pgfplotsset{colorscheme/rocket} - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %%%%%%% Actual Document %%%%%%%%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - - \\title{Banking Report} - \\author{} - - - \\begin{document} - - \\maketitle - - Test - - - \\end{document} - """) - - return result - - -def build_document(src: str): - os.makedirs(os.path.dirname("build/doc.tex"), exist_ok=True) - with open("build/doc.tex", "w") as out_file: - out_file.write(src) - - shutil.copyfile("res/.latexmkrc", "build/.latexmkrc") - - subprocess.call("docker build . -f res/Dockerfile.alpine" - " -t banking-breakdown", - shell=True) - subprocess.call("docker run --rm -u `id -u`:`id -g`" - " -v $(realpath .):/project" - " -w /project/build banking-breakdown" - " latexmk -pdf doc.tex", - shell=True) diff --git a/banking_breakdown/types.py b/banking_breakdown/types.py new file mode 100644 index 0000000..c314506 --- /dev/null +++ b/banking_breakdown/types.py @@ -0,0 +1,9 @@ +from dataclasses import dataclass +import pandas as pd + + +@dataclass +class ReportData: + category_overview: pd.DataFrame + net_income: pd.DataFrame + total_value: pd.DataFrame diff --git a/res/Dockerfile.alpine b/res/Dockerfile.alpine index 7fc51d8..6d3698e 100644 --- a/res/Dockerfile.alpine +++ b/res/Dockerfile.alpine @@ -2,3 +2,6 @@ FROM alpine:3.19 RUN apk update && apk upgrade RUN apk add make texlive texmf-dist-pictures +RUN apk add texmf-dist-fontsextra +RUN apk add texmf-dist-latexextra + diff --git a/res/report.tex b/res/report.tex new file mode 100644 index 0000000..ddc0a9d --- /dev/null +++ b/res/report.tex @@ -0,0 +1,170 @@ +\documentclass{article} + +% Packages necessary for common.tex +\usepackage{amsmath} +\usepackage{pgfplots} +\pgfplotsset{compat=newest} + +% Other packages +\usepackage[a4paper, total={12cm, 25cm}]{geometry} +\usepackage{float} +\usepackage{calc} +\usepackage{ifthen} +\usepackage{tikz} +\usepackage[l3]{csvsimple} +\usepackage{subcaption} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%% Set common options %%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\input{../lib/latex-common/common.tex} +\pgfplotsset{colorscheme/rocket} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Define Custom Commands %%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\newcommand{\slice}[6]{ + \pgfmathparse{0.5*#1+0.5*#2} + \let\midangle\pgfmathresult + + % slice + \fill[thick,color=#5] (0,0) -- (#1:1) arc (#1:#2+1:1) -- (0,0); + + % outer label + \node[label=\midangle:#4] at (\midangle:1) {}; + + % inner label + \pgfmathparse{min((#2-#1-10)/110*(-0.3),0)} + \let\temp\pgfmathresult + \pgfmathparse{max(\temp,-0.5) + 0.8} + \let\innerpos\pgfmathresult + \node[color=#6] at (\midangle:\innerpos) {#3}; +} + +\newcounter{pieElem} +\newcounter{pieSliceA} +\newcounter{pieSliceB} +\newcommand{\pie}[1]{ + % Count elements + \setcounter{pieElem}{0}% + \foreach\pieElem in {#1}{\stepcounter{pieElem}}% + \edef\numElements{\arabic{pieElem}} + + % Draw pie chart + \setcounter{pieSliceA}{0}% + \setcounter{pieSliceB}{0}% + \foreach \xi/\t [count=\xk from 0] in {#1} { + % Get colors + \pgfmathparse{1000 / (\numElements - 1) * \xk} + \extractcolormapcolor{pieColor\xk}{\pgfmathresult} + \pgfmathparse{(\xk / \numElements < 0.5)*1000} + \extractcolormapcolor{pieTextColor\xk}{\pgfmathresult} + + % Draw slice + \setcounter{pieSliceA}{\thepieSliceB} + \addtocounter{pieSliceB}{\xi} + \slice{\thepieSliceA/100*360}{\thepieSliceB/100*360}{\xi\%}{\t}{pieColor\xk}{pieTextColor\xk} + } +} + +\newcommand{\csvPie}[1]{ + % Count elements + \setcounter{pieElem}{0}% + \csvreader[head to column names]{#1}{}{% + \stepcounter{pieElem} + } + \edef\numElements{\arabic{pieElem}} + + % Draw pie chart + \setcounter{pieElem}{0}% + \setcounter{pieSliceA}{0}% + \setcounter{pieSliceB}{0}% + \csvreader[head to column names]{#1}{}{% + % Get colors + \pgfmathparse{1000 / (\numElements - 1) * \thepieElem} + \extractcolormapcolor{pieColor\thepieElem}{\pgfmathresult} + \pgfmathparse{(\thepieElem / \numElements < 0.5)*1000} + \extractcolormapcolor{pieTextColor\thepieElem}{\pgfmathresult} + + % Draw slice + \setcounter{pieSliceA}{\thepieSliceB} + \addtocounter{pieSliceB}{\value} + \slice{\thepieSliceA/100*360}{\thepieSliceB/100*360}{\value\%}{\category} + {pieColor\thepieElem}{pieTextColor\thepieElem} + + \stepcounter{pieElem} + } +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Actual Document %%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\title{Expense Report\vspace{-1cm}} +\author{} + + +\begin{document} + +\maketitle +\vspace{1cm} + +\begin{figure}[H] + \centering + + \begin{tikzpicture}[scale=3] + \csvPie{category_overview.csv} + \end{tikzpicture} +\end{figure} + +\begin{figure}[H] + \centering + + \begin{subfigure}[t]{\textwidth} + \centering + + \begin{tikzpicture} + \begin{axis}[ + width=\textwidth, + height=0.375\textwidth, + ylabel={Net income}, + y label style={at={(-0.1,0.5)},anchor=south}, + ] + \addplot+[ybar, color=scol2, fill=scol2, line width=1pt] + table[col sep=comma, x=t, y=value, discard if lt={value}{0}] + {net_income.csv}; + \addplot+[ybar, color=scol0, fill=scol0, line width=1pt] + table[col sep=comma, x=t, y=value, discard if gt={value}{0}] + {net_income.csv}; + \end{axis} + \end{tikzpicture} + \end{subfigure} + \begin{subfigure}[t]{\textwidth} + \centering + + \begin{tikzpicture} + \begin{axis}[ + width=\textwidth, + height=0.375\textwidth, + area style, + ylabel={Total amount}, + y label style={at={(-0.1,0.5)},anchor=south}, + ] + \addplot+[mark=none, color=scol1, line width=1pt] + table[col sep=comma, x=t, y=value] + {total_value.csv} \closedcycle; + \end{axis} + \end{tikzpicture} + \end{subfigure} +\end{figure} + +\end{document} +