diff --git a/.gitignore b/.gitignore
index 5fc9974..303f9f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,11 +4,11 @@ web/node_modules
 **/__pycache__/**
 log.txt
 .env
-api/tests/products_data.py
+api/src/tests/products_data.py
 /api/algo_test_report.png
 /api/report.html
 /api/report.pdf
 /api/test_report.png
 /api/.coverage
-/api/test_data/interpolate_input.csv
+/api/src/test_data/interpolate_input.csv
 .python-version
\ No newline at end of file
diff --git a/api/Dockerfile b/api/Dockerfile
index 2eb26fc..d965264 100644
--- a/api/Dockerfile
+++ b/api/Dockerfile
@@ -1,8 +1,8 @@
 FROM python:3.10-slim
 
 ENV PYTHONUNBUFFERED=1
-ENV FLASK_APP=/app/app.py
-ENV PYTHONPATH=/app
+ENV FLASK_APP=/app/src/app.py
+ENV PYTHONPATH=/app/src
 ENV PATH="/app/.venv/bin:${PATH}"
 
 RUN pip install --upgrade pip && \
@@ -15,10 +15,8 @@ RUN apt-get update && apt-get install -y \
     texlive-latex-base \
     texlive-fonts-extra \
     texlive-fonts-recommended \
-    texlive-latex-extra \
-    wkhtmltopdf
+    texlive-latex-extra
 
-#RUN apt-get install -y libblas-dev liblapack-dev
 
 WORKDIR /app
 RUN chown -R 1000:1000 /app
diff --git a/api/docker-entrypoint.sh b/api/docker-entrypoint.sh
index 7795915..57c4686 100755
--- a/api/docker-entrypoint.sh
+++ b/api/docker-entrypoint.sh
@@ -12,7 +12,7 @@ if [ "$1" = 'api' ]; then
             --log-file=- \
             --workers 4 \
             --timeout 60 \
-            app:app
+            src/app:app
     fi
 else
   exec "$@"
diff --git a/api/poetry.lock b/api/poetry.lock
index f523053..95e5c52 100644
--- a/api/poetry.lock
+++ b/api/poetry.lock
@@ -1212,18 +1212,6 @@ files = [
     {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"},
 ]
 
-[[package]]
-name = "pdfkit"
-version = "1.0.0"
-description = "Wkhtmltopdf python wrapper to convert html to pdf using the webkit rendering engine and qt"
-optional = false
-python-versions = "*"
-files = [
-    {file = "pdfkit-1.0.0-py2-none-any.whl", hash = "sha256:cc122e5aed594198ff7aaa566f2950d2163763576ab891c161bb1f6c630f5a8e"},
-    {file = "pdfkit-1.0.0-py3-none-any.whl", hash = "sha256:a7a4ca0d978e44fa8310c4909f087052430a6e8e0b1dd7ceef657f139789f96f"},
-    {file = "pdfkit-1.0.0.tar.gz", hash = "sha256:992f821e1e18fc8a0e701ecae24b51a2d598296a180caee0a24c0af181da02a9"},
-]
-
 [[package]]
 name = "pillow"
 version = "10.1.0"
@@ -1293,13 +1281,13 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
 
 [[package]]
 name = "platformdirs"
-version = "4.0.0"
+version = "4.1.0"
 description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"},
-    {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"},
+    {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"},
+    {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"},
 ]
 
 [package.extras]
@@ -1364,17 +1352,6 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
 docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
 tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
 
-[[package]]
-name = "pypandoc"
-version = "1.12"
-description = "Thin wrapper for pandoc."
-optional = false
-python-versions = ">=3.6"
-files = [
-    {file = "pypandoc-1.12-py3-none-any.whl", hash = "sha256:efb4f7d68ead8bec32e22b62f02d5608a1700978b51bfc4af286fd6acfe9d218"},
-    {file = "pypandoc-1.12.tar.gz", hash = "sha256:8f44740a9f074e121d81b489f073160421611d4ead62d1b306aeb11aab3c32df"},
-]
-
 [[package]]
 name = "pyparsing"
 version = "3.1.1"
@@ -1525,28 +1502,28 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
 
 [[package]]
 name = "ruff"
-version = "0.1.6"
+version = "0.1.7"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "ruff-0.1.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:88b8cdf6abf98130991cbc9f6438f35f6e8d41a02622cc5ee130a02a0ed28703"},
-    {file = "ruff-0.1.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c549ed437680b6105a1299d2cd30e4964211606eeb48a0ff7a93ef70b902248"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cf5f701062e294f2167e66d11b092bba7af6a057668ed618a9253e1e90cfd76"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:05991ee20d4ac4bb78385360c684e4b417edd971030ab12a4fbd075ff535050e"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87455a0c1f739b3c069e2f4c43b66479a54dea0276dd5d4d67b091265f6fd1dc"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:683aa5bdda5a48cb8266fcde8eea2a6af4e5700a392c56ea5fb5f0d4bfdc0240"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:137852105586dcbf80c1717facb6781555c4e99f520c9c827bd414fac67ddfb6"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd98138a98d48a1c36c394fd6b84cd943ac92a08278aa8ac8c0fdefcf7138f35"},
-    {file = "ruff-0.1.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0cd909d25f227ac5c36d4e7e681577275fb74ba3b11d288aff7ec47e3ae745"},
-    {file = "ruff-0.1.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8fd1c62a47aa88a02707b5dd20c5ff20d035d634aa74826b42a1da77861b5ff"},
-    {file = "ruff-0.1.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fd89b45d374935829134a082617954120d7a1470a9f0ec0e7f3ead983edc48cc"},
-    {file = "ruff-0.1.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:491262006e92f825b145cd1e52948073c56560243b55fb3b4ecb142f6f0e9543"},
-    {file = "ruff-0.1.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ea284789861b8b5ca9d5443591a92a397ac183d4351882ab52f6296b4fdd5462"},
-    {file = "ruff-0.1.6-py3-none-win32.whl", hash = "sha256:1610e14750826dfc207ccbcdd7331b6bd285607d4181df9c1c6ae26646d6848a"},
-    {file = "ruff-0.1.6-py3-none-win_amd64.whl", hash = "sha256:4558b3e178145491e9bc3b2ee3c4b42f19d19384eaa5c59d10acf6e8f8b57e33"},
-    {file = "ruff-0.1.6-py3-none-win_arm64.whl", hash = "sha256:03910e81df0d8db0e30050725a5802441c2022ea3ae4fe0609b76081731accbc"},
-    {file = "ruff-0.1.6.tar.gz", hash = "sha256:1b09f29b16c6ead5ea6b097ef2764b42372aebe363722f1605ecbcd2b9207184"},
+    {file = "ruff-0.1.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7f80496854fdc65b6659c271d2c26e90d4d401e6a4a31908e7e334fab4645aac"},
+    {file = "ruff-0.1.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1ea109bdb23c2a4413f397ebd8ac32cb498bee234d4191ae1a310af760e5d287"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0c2de9dd9daf5e07624c24add25c3a490dbf74b0e9bca4145c632457b3b42a"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:69a4bed13bc1d5dabf3902522b5a2aadfebe28226c6269694283c3b0cecb45fd"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de02ca331f2143195a712983a57137c5ec0f10acc4aa81f7c1f86519e52b92a1"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45b38c3f8788a65e6a2cab02e0f7adfa88872696839d9882c13b7e2f35d64c5f"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c64cb67b2025b1ac6d58e5ffca8f7b3f7fd921f35e78198411237e4f0db8e73"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dcc6bb2f4df59cb5b4b40ff14be7d57012179d69c6565c1da0d1f013d29951b"},
+    {file = "ruff-0.1.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2bb4bb6bbe921f6b4f5b6fdd8d8468c940731cb9406f274ae8c5ed7a78c478"},
+    {file = "ruff-0.1.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:276a89bcb149b3d8c1b11d91aa81898fe698900ed553a08129b38d9d6570e717"},
+    {file = "ruff-0.1.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:90c958fe950735041f1c80d21b42184f1072cc3975d05e736e8d66fc377119ea"},
+    {file = "ruff-0.1.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6b05e3b123f93bb4146a761b7a7d57af8cb7384ccb2502d29d736eaade0db519"},
+    {file = "ruff-0.1.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:290ecab680dce94affebefe0bbca2322a6277e83d4f29234627e0f8f6b4fa9ce"},
+    {file = "ruff-0.1.7-py3-none-win32.whl", hash = "sha256:416dfd0bd45d1a2baa3b1b07b1b9758e7d993c256d3e51dc6e03a5e7901c7d80"},
+    {file = "ruff-0.1.7-py3-none-win_amd64.whl", hash = "sha256:4af95fd1d3b001fc41325064336db36e3d27d2004cdb6d21fd617d45a172dd96"},
+    {file = "ruff-0.1.7-py3-none-win_arm64.whl", hash = "sha256:0683b7bfbb95e6df3c7c04fe9d78f631f8e8ba4868dfc932d43d690698057e2e"},
+    {file = "ruff-0.1.7.tar.gz", hash = "sha256:dffd699d07abf54833e5f6cc50b85a6ff043715da8788c4a79bcd4ab4734d306"},
 ]
 
 [[package]]
@@ -1857,4 +1834,4 @@ multidict = ">=4.0"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.10"
-content-hash = "98d1b0f822b3b2aa14ddb526db5f953fd6ba7524eb6606bc9c70353e4fcf7fad"
+content-hash = "340dacf58c9753f5091a16f36e18ea089b33939edd87fa558ba0168cb910d3a2"
diff --git a/api/pyproject.toml b/api/pyproject.toml
index 70cd504..1985bf7 100644
--- a/api/pyproject.toml
+++ b/api/pyproject.toml
@@ -13,10 +13,8 @@ xlrd = "^2.0.1"
 azure-storage-blob = "^12.19.0"
 PyJWT = "^2.8.0"
 cachetools = "^5.3.2"
-pypandoc = "^1.12"
 vcrpy = "^5.1.0"
 matplotlib = "^3.8.2"
-pdfkit = "^1.0.0"
 gunicorn = "^21.2.0"
 Flask = "^3.0.0"
 
diff --git a/api/__init__.py b/api/src/__init__.py
similarity index 100%
rename from api/__init__.py
rename to api/src/__init__.py
diff --git a/api/app.py b/api/src/app.py
similarity index 99%
rename from api/app.py
rename to api/src/app.py
index b794e03..6f79114 100644
--- a/api/app.py
+++ b/api/src/app.py
@@ -1,5 +1,7 @@
 import traceback
 
+from flask import Flask, Response, request, send_file
+
 from calculators.bridge import SIZE_STEPS
 from config import Config
 from controllers.combination import bridge_from_combination
@@ -7,7 +9,6 @@
 from controllers.optimizer import optimizer_request_handler
 from controllers.products import products_get
 from controllers.report import create_report
-from flask import Flask, Response, request, send_file
 from util.authentication import authorize
 from util.sync_share_point_az import sync_all
 
diff --git a/api/calculators/bridge.py b/api/src/calculators/bridge.py
similarity index 99%
rename from api/calculators/bridge.py
rename to api/src/calculators/bridge.py
index f0ce572..758b5bc 100644
--- a/api/calculators/bridge.py
+++ b/api/src/calculators/bridge.py
@@ -1,6 +1,7 @@
 from math import sqrt
 
 from cachetools import LFUCache, cached
+
 from classes.product import Product
 from util.enums import BridgeOption
 
diff --git a/api/calculators/fraction_interpolator.py b/api/src/calculators/fraction_interpolator.py
similarity index 99%
rename from api/calculators/fraction_interpolator.py
rename to api/src/calculators/fraction_interpolator.py
index 3a5bf1f..7642fff 100644
--- a/api/calculators/fraction_interpolator.py
+++ b/api/src/calculators/fraction_interpolator.py
@@ -1,9 +1,10 @@
 import csv
 from copy import copy
 
-from calculators.bridge import SIZE_STEPS
 from numpy import log
 
+from calculators.bridge import SIZE_STEPS
+
 
 def lookup_smaller(table: dict, value: float):
     n = [i for i in table.keys() if i <= value]
diff --git a/api/calculators/optimizer.py b/api/src/calculators/optimizer.py
similarity index 99%
rename from api/calculators/optimizer.py
rename to api/src/calculators/optimizer.py
index 7c97f9b..40c5fa0 100644
--- a/api/calculators/optimizer.py
+++ b/api/src/calculators/optimizer.py
@@ -5,6 +5,7 @@
 
 import numpy as np
 from cachetools import LFUCache, cached
+
 from calculators.bridge import SIZE_STEPS, calculate_blend_cumulative
 from classes.product import Product
 
diff --git a/api/classes/product.py b/api/src/classes/product.py
similarity index 100%
rename from api/classes/product.py
rename to api/src/classes/product.py
diff --git a/api/classes/user.py b/api/src/classes/user.py
similarity index 100%
rename from api/classes/user.py
rename to api/src/classes/user.py
diff --git a/api/config.py b/api/src/config.py
similarity index 100%
rename from api/config.py
rename to api/src/config.py
diff --git a/api/controllers/__init__.py b/api/src/controllers/__init__.py
similarity index 100%
rename from api/controllers/__init__.py
rename to api/src/controllers/__init__.py
diff --git a/api/controllers/combination.py b/api/src/controllers/combination.py
similarity index 100%
rename from api/controllers/combination.py
rename to api/src/controllers/combination.py
diff --git a/api/controllers/optimal_bridge.py b/api/src/controllers/optimal_bridge.py
similarity index 99%
rename from api/controllers/optimal_bridge.py
rename to api/src/controllers/optimal_bridge.py
index a3f16e9..f7a3bd8 100644
--- a/api/controllers/optimal_bridge.py
+++ b/api/src/controllers/optimal_bridge.py
@@ -1,6 +1,7 @@
-from calculators.bridge import theoretical_bridge
 from flask import Response
 
+from calculators.bridge import theoretical_bridge
+
 
 def bridgeRequestHandler(option: str, value: int):
     if not value or not option:
diff --git a/api/controllers/optimizer.py b/api/src/controllers/optimizer.py
similarity index 99%
rename from api/controllers/optimizer.py
rename to api/src/controllers/optimizer.py
index 8315a9c..c1866cf 100644
--- a/api/controllers/optimizer.py
+++ b/api/src/controllers/optimizer.py
@@ -1,8 +1,9 @@
+from flask import Response
+
 from calculators.bridge import theoretical_bridge
 from calculators.optimizer import Optimizer
 from classes.product import Product
 from controllers.products import products_get
-from flask import Response
 
 
 def optimizer_request_handler(
diff --git a/api/controllers/products.py b/api/src/controllers/products.py
similarity index 99%
rename from api/controllers/products.py
rename to api/src/controllers/products.py
index f356ec8..743abed 100644
--- a/api/controllers/products.py
+++ b/api/src/controllers/products.py
@@ -1,6 +1,7 @@
 from cachetools import TTLCache, cached
-from config import Config
 from flask import Response
+
+from config import Config
 from util.azure_table import get_service
 
 
diff --git a/api/controllers/report.py b/api/src/controllers/report.py
similarity index 85%
rename from api/controllers/report.py
rename to api/src/controllers/report.py
index abcb02e..9a3c122 100644
--- a/api/controllers/report.py
+++ b/api/src/controllers/report.py
@@ -1,7 +1,7 @@
+import subprocess
+import tempfile
 from datetime import datetime
 
-import pypandoc
-from config import Config
 from plots.bridge import bridge_plot
 from plots.evolution import evolution_plot
 from plots.products_pie import products_pie
@@ -91,25 +91,27 @@ def as_html(report: Report, pie_chart, bridge_graph, fitness_plot) -> str:
 </body>"""
 
 
+OUT_PDF = "/tmp/report.pdf"  # noqa: S108
+
+
 def create_report(request: dict, bridge: bool = True):
     report: Report = Report().from_dict(request)
     pie_chart = products_pie(report.products)
     bridge_graph = bridge_plot(report.products, report.bridging_mode, report.bridging_value) if bridge else ""
     fitness_plot = evolution_plot(report.curve)
     html = as_html(report, pie_chart, bridge_graph, fitness_plot)
-    with open(f"{Config.HOME_DIR}/report.html", "w") as report_html:
-        report_html.write(html)
-    pypandoc.convert_file(
-        f"{Config.HOME_DIR}/report.html",
-        "pdf",
-        format="html",
-        outputfile=f"{Config.HOME_DIR}/report.pdf",
-        extra_args=[
-            "--metadata title='LCM Report'" "--to",
-            "html",
-            "--css",
-            f"{Config.HOME_DIR}/util/report.css",
-            "--pdf-engine-opt=--enable-local-file-access",
-        ],
-    )
-    return f"{Config.HOME_DIR}/report.pdf"
+
+    with tempfile.NamedTemporaryFile("w") as html_file:
+        html_file.write(html)
+        try:
+            subprocess.run(
+                [f"pandoc {html_file.name} -f html -o {OUT_PDF}"],
+                check=True,
+                shell=True,  # noqa: S602
+                capture_output=True,
+            )
+        except subprocess.CalledProcessError as ex:
+            print(ex.stderr.decode())
+            raise ex
+
+    return OUT_PDF
diff --git a/api/plots/bridge.py b/api/src/plots/bridge.py
similarity index 99%
rename from api/plots/bridge.py
rename to api/src/plots/bridge.py
index e9798c7..919f754 100644
--- a/api/plots/bridge.py
+++ b/api/src/plots/bridge.py
@@ -2,10 +2,11 @@
 from io import BytesIO
 
 import matplotlib.pyplot as plt
+from matplotlib.ticker import ScalarFormatter
+
 from calculators.bridge import SIZE_STEPS, calculate_blend_cumulative, theoretical_bridge
 from classes.product import Product
 from controllers.products import products_get
-from matplotlib.ticker import ScalarFormatter
 
 
 def bridge_plot(products: dict, mode, value) -> str:
diff --git a/api/plots/evolution.py b/api/src/plots/evolution.py
similarity index 100%
rename from api/plots/evolution.py
rename to api/src/plots/evolution.py
diff --git a/api/plots/products_pie.py b/api/src/plots/products_pie.py
similarity index 100%
rename from api/plots/products_pie.py
rename to api/src/plots/products_pie.py
diff --git a/api/test_data/flow-carb10.xlsx b/api/src/test_data/flow-carb10.xlsx
similarity index 100%
rename from api/test_data/flow-carb10.xlsx
rename to api/src/test_data/flow-carb10.xlsx
diff --git a/api/test_data/metadata.csv b/api/src/test_data/metadata.csv
similarity index 100%
rename from api/test_data/metadata.csv
rename to api/src/test_data/metadata.csv
diff --git a/api/tests/__init__.py b/api/src/tests/__init__.py
similarity index 100%
rename from api/tests/__init__.py
rename to api/src/tests/__init__.py
diff --git a/api/tests/create_product_row_test.py b/api/src/tests/create_product_row_test.py
similarity index 100%
rename from api/tests/create_product_row_test.py
rename to api/src/tests/create_product_row_test.py
diff --git a/api/tests/optimizer_test.py b/api/src/tests/optimizer_test.py
similarity index 99%
rename from api/tests/optimizer_test.py
rename to api/src/tests/optimizer_test.py
index 2d9d06a..37634d1 100644
--- a/api/tests/optimizer_test.py
+++ b/api/src/tests/optimizer_test.py
@@ -2,10 +2,11 @@
 
 import matplotlib.pyplot as plt
 import numpy as np
+from matplotlib.ticker import ScalarFormatter
+
 from calculators.bridge import SIZE_STEPS, theoretical_bridge
 from calculators.optimizer import Optimizer
 from config import Config
-from matplotlib.ticker import ScalarFormatter
 from util.enums import BridgeOption
 
 
diff --git a/api/tests/test_calculate_performance.py b/api/src/tests/test_calculate_performance.py
similarity index 100%
rename from api/tests/test_calculate_performance.py
rename to api/src/tests/test_calculate_performance.py
diff --git a/api/tests/test_interpolator.py b/api/src/tests/test_interpolator.py
similarity index 100%
rename from api/tests/test_interpolator.py
rename to api/src/tests/test_interpolator.py
diff --git a/api/tests/test_report.py b/api/src/tests/test_report.py
similarity index 100%
rename from api/tests/test_report.py
rename to api/src/tests/test_report.py
diff --git a/api/tests/utils.py b/api/src/tests/utils.py
similarity index 100%
rename from api/tests/utils.py
rename to api/src/tests/utils.py
diff --git a/api/src/util/__init__.py b/api/src/util/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/api/util/authentication.py b/api/src/util/authentication.py
similarity index 99%
rename from api/util/authentication.py
rename to api/src/util/authentication.py
index b4edc47..3c6d191 100644
--- a/api/util/authentication.py
+++ b/api/src/util/authentication.py
@@ -4,10 +4,11 @@
 import jwt
 import requests
 from cachetools import TTLCache, cached
-from classes.user import User
-from config import Config
 from flask import abort, g, request
 from jwt.algorithms import RSAAlgorithm
+
+from classes.user import User
+from config import Config
 from util.exceptions import AuthenticationException
 
 
diff --git a/api/util/azure_blobs.py b/api/src/util/azure_blobs.py
similarity index 99%
rename from api/util/azure_blobs.py
rename to api/src/util/azure_blobs.py
index 619afcd..17a1668 100644
--- a/api/util/azure_blobs.py
+++ b/api/src/util/azure_blobs.py
@@ -3,10 +3,11 @@
 from pathlib import Path
 
 from azure.storage.blob import BlobProperties, ContainerClient
+from xlrd.sheet import Sheet
+
 from config import Config
 from util.azure_table import process_meta_blob, sanitize_row_key
 from util.excel import excel_raw_file_to_sheet, sheet_to_bridge_dict
-from xlrd.sheet import Sheet
 
 
 def get_container_client() -> ContainerClient:
diff --git a/api/util/azure_table.py b/api/src/util/azure_table.py
similarity index 99%
rename from api/util/azure_table.py
rename to api/src/util/azure_table.py
index 3625690..a5e5709 100644
--- a/api/util/azure_table.py
+++ b/api/src/util/azure_table.py
@@ -6,6 +6,7 @@
 
 from azure.common import AzureConflictHttpError
 from azure.cosmosdb.table.tableservice import TableService
+
 from config import Config
 
 
diff --git a/api/util/enums.py b/api/src/util/enums.py
similarity index 100%
rename from api/util/enums.py
rename to api/src/util/enums.py
diff --git a/api/util/excel.py b/api/src/util/excel.py
similarity index 100%
rename from api/util/excel.py
rename to api/src/util/excel.py
diff --git a/api/util/exceptions.py b/api/src/util/exceptions.py
similarity index 100%
rename from api/util/exceptions.py
rename to api/src/util/exceptions.py
diff --git a/api/util/sync_share_point_az.py b/api/src/util/sync_share_point_az.py
similarity index 99%
rename from api/util/sync_share_point_az.py
rename to api/src/util/sync_share_point_az.py
index 41fa42b..0f37b2d 100644
--- a/api/util/sync_share_point_az.py
+++ b/api/src/util/sync_share_point_az.py
@@ -4,8 +4,9 @@
 import requests
 from azure.common import AzureMissingResourceHttpError
 from azure.cosmosdb.table.tableservice import TableBatch, TableService
-from config import Config
 from urllib3.exceptions import HeaderParsingError
+
+from config import Config
 from util.azure_blobs import get_metadata_blob_data, get_product_blobs_data
 from util.azure_table import create_table
 
diff --git a/api/util/utils.py b/api/src/util/utils.py
similarity index 100%
rename from api/util/utils.py
rename to api/src/util/utils.py
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 46281e3..9645e11 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -19,8 +19,8 @@ services:
       TABLE_KEY: ${TABLE_KEY}
     ports:
       - "5000:5000"
-#    volumes:
-#      - ./api/:/app
+    volumes:
+      - ./api/src:/app/src
 
   web:
     build:
diff --git a/web/src/Components/Combinations/CombinationCard.tsx b/web/src/Components/Combinations/CombinationCard.tsx
index f8db825..87a1517 100644
--- a/web/src/Components/Combinations/CombinationCard.tsx
+++ b/web/src/Components/Combinations/CombinationCard.tsx
@@ -1,6 +1,6 @@
 import React, { useContext, useEffect, useState } from 'react'
 // @ts-ignore
-import { Button, Icon, Switch, Input} from '@equinor/eds-core-react'
+import { Button, Icon, Switch, Input } from '@equinor/eds-core-react'
 import CombinationTable from './CombinationTable'
 import styled from 'styled-components'
 import { Card } from './CardContainer'
@@ -131,7 +131,9 @@ export const CombinationCard = ({
     <Card>
       <div>
         <CardHeader>
-          <Button variant='ghost_icon' onClick={()=>setIsHeaderEditable(!isHeaderEditable)}><Icon  name='edit' size={16} /></Button>
+          <Button variant='ghost_icon' onClick={() => setIsHeaderEditable(!isHeaderEditable)}>
+            <Icon name='edit' size={16} />
+          </Button>
           <Input
             id={`${combination.name}`}
             value={combinationName}
diff --git a/web/src/Components/Common/EditProducts.tsx b/web/src/Components/Common/EditProducts.tsx
index 9cf9d2b..f7c558c 100644
--- a/web/src/Components/Common/EditProducts.tsx
+++ b/web/src/Components/Common/EditProducts.tsx
@@ -4,7 +4,6 @@ import { Button, Dialog, Icon, Scrim } from '@equinor/eds-core-react'
 import SelectProducts from '../SelectProducts'
 import { Products } from '../../Types'
 
-
 interface AddProductsProps {
   allProducts: Products
   enabledProducts: Products
@@ -26,8 +25,7 @@ export const EditProducts = ({ allProducts, enabledProducts, setEnabledProducts
       </Button>
       <Dialog style={{ width: 'min-content' }} open={dialogOpen}>
         <Dialog.Header>
-
-        <Dialog.Title>Select products in blend</Dialog.Title>
+          <Dialog.Title>Select products in blend</Dialog.Title>
         </Dialog.Header>
         <Dialog.CustomContent style={{ display: 'flex', flexFlow: 'column', alignItems: 'center' }}>
           <SelectProducts
diff --git a/web/src/Components/Common/Tooltip.tsx b/web/src/Components/Common/Tooltip.tsx
index 69d8e98..0140a52 100644
--- a/web/src/Components/Common/Tooltip.tsx
+++ b/web/src/Components/Common/Tooltip.tsx
@@ -2,7 +2,7 @@ import styled from 'styled-components'
 import React, { ReactElement } from 'react'
 // @ts-ignore
 import { Tooltip as EDSTooltip, Icon } from '@equinor/eds-core-react'
-import { help_outline} from '@equinor/eds-icons'
+import { help_outline } from '@equinor/eds-icons'
 import { COLORS } from '../../Enums'
 
 const Wrapper = styled.div`
@@ -29,7 +29,7 @@ export const Tooltip = ({ text, children }: TooltipProps): ReactElement => {
       {children}
       <EDSTooltip title={text} placement={'top-end'}>
         <Wrapper>
-        <Icon data={help_outline} size={18} style={{color: COLORS.secondary}}/>
+          <Icon data={help_outline} size={18} style={{ color: COLORS.secondary }} />
         </Wrapper>
       </EDSTooltip>
     </TooltipWrapper>
diff --git a/web/src/Components/ContactButton.tsx b/web/src/Components/ContactButton.tsx
index 41dc212..5aab355 100644
--- a/web/src/Components/ContactButton.tsx
+++ b/web/src/Components/ContactButton.tsx
@@ -2,7 +2,6 @@ import React, { useState } from 'react'
 // @ts-ignore
 import { Button, Dialog, Icon } from '@equinor/eds-core-react'
 
-
 export const ContactButton = () => {
   const [dialogOpen, setDialogOpen] = useState<boolean>(false)
 
@@ -14,8 +13,7 @@ export const ContactButton = () => {
       </Button>
       <Dialog style={{ width: 'min-content' }} open={dialogOpen}>
         <Dialog.Header>
-
-        <Dialog.Title>Contact and support</Dialog.Title>
+          <Dialog.Title>Contact and support</Dialog.Title>
         </Dialog.Header>
         <Dialog.CustomContent style={{ display: 'flex', flexFlow: 'column', alignItems: 'center', width: '500px' }}>
           <p>
diff --git a/web/src/Components/Optimization/OptimizationRunner.tsx b/web/src/Components/Optimization/OptimizationRunner.tsx
index b839dc4..a2ced34 100644
--- a/web/src/Components/Optimization/OptimizationRunner.tsx
+++ b/web/src/Components/Optimization/OptimizationRunner.tsx
@@ -106,9 +106,9 @@ const OptimizationRunner = ({ mode, value, handleUpdate, allProducts }: Optimiza
           onClick={() => setDialogOpen(true)}
         />
         <Dialog style={{ width: 'auto' }} open={dialogOpen}>
-                  <Dialog.Header>
-          <Dialog.Title>Formulas used in optimizer</Dialog.Title>
-        </Dialog.Header>
+          <Dialog.Header>
+            <Dialog.Title>Formulas used in optimizer</Dialog.Title>
+          </Dialog.Header>
           <Dialog.CustomContent>
             <table style={{ border: '50px' }}>
               <tr>
diff --git a/web/src/Components/RefreshButton.tsx b/web/src/Components/RefreshButton.tsx
index ea943a5..1dfdc3c 100644
--- a/web/src/Components/RefreshButton.tsx
+++ b/web/src/Components/RefreshButton.tsx
@@ -9,7 +9,6 @@ import { AuthContext } from 'react-oauth2-code-pkce'
 import Icon from '../Icons'
 import { IAuthContext } from 'react-oauth2-code-pkce'
 
-
 const ButtonWrapper = styled.div`
   display: flex;
   justify-content: space-between;
@@ -42,8 +41,7 @@ export const RefreshButton = () => {
       </Button>
       <Dialog style={{ width: 'min-content' }} open={dialogOpen} isDismissable={true}>
         <Dialog.Header>
-
-        <Dialog.Title>Synchronize SharePoint data</Dialog.Title>
+          <Dialog.Title>Synchronize SharePoint data</Dialog.Title>
         </Dialog.Header>
         <Dialog.CustomContent style={{ display: 'flex', flexFlow: 'column', alignItems: 'center' }}>
           <p>
diff --git a/web/src/Pages/Main.tsx b/web/src/Pages/Main.tsx
index cb8b937..5547f31 100644
--- a/web/src/Pages/Main.tsx
+++ b/web/src/Pages/Main.tsx
@@ -13,6 +13,7 @@ import { ErrorToast } from '../Components/Common/Toast'
 import { AuthContext } from 'react-oauth2-code-pkce'
 import { ContactButton } from '../Components/ContactButton'
 import { IAuthContext } from 'react-oauth2-code-pkce'
+import { info_circle, open_in_browser } from '@equinor/eds-icons'
 
 const Body = styled.div`
   display: flex;
@@ -59,9 +60,13 @@ export default (): ReactElement => {
             }}
           >
             <div>
-              <StyledLink href={'https://statoilsrm.sharepoint.com/sites/LCMlibrary/Lists/Product'}>
+              <StyledLink
+                href='https://statoilsrm.sharepoint.com/sites/LCMlibrary/Lists/Product'
+                target='_blank'
+                rel='noopener noreferrer'
+              >
                 <Button variant='outlined'>
-                  <Icon name='info_circle' title='info_circle' />
+                  <Icon data={info_circle} title='info_circle' />
                   LCM Library SharePoint
                 </Button>
               </StyledLink>