Repository: 1kastner/conflowgen (language: python)

Issue:
Add running all analyses to the documentation
See #74 for reference. Just use the run_all_analyses instead in a separate Jupyter Notebook.


Discussion / hints:

Fix patch:
diff --git a/.flake8_nb b/.flake8_nb
new file mode 100644
index 00000000..4afe617d
--- /dev/null
+++ b/.flake8_nb
@@ -0,0 +1,16 @@
+[flake8_nb]
+
+exclude =
+    *.git*
+    *venv*
+    *.ipynb_checkpoints*
+    *.virtual_documents*
+    *build*
+    *.py
+
+ignore =
+    F821  # Variables such as 'In' or 'display' are not detected and variables of imported Jupyter Notebook cannot be detected
+    E501  # Allow long lines
+    W503  # Long lines need to be broken somewhere and otherwise W504 is violated
+
+show_source = True
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index 2ce125eb..4ce544bf 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -33,6 +33,6 @@ jobs:
       run: |
         pylint conflowgen && pylint setup.py
 
-    - name: Check code quality with flake8
+    - name: Check code quality with flake8 and flake8_nb
       run: |
-        flake8
+        flake8 && flake8_nb
diff --git a/Contributing.md b/Contributing.md
index d6438108..6dd568a8 100644
--- a/Contributing.md
+++ b/Contributing.md
@@ -3,6 +3,12 @@
 If you find bugs, errors, omissions or other things that need improvement, please create an issue or a pull request at 
 [https://github.com/1kastner/conflowgen/](https://github.com/1kastner/conflowgen/).
 Contributions are always welcome!
+We would love to see ConFlowGen grow in different dimensions, including (but not limited to)...
+- ...adding reliable data sources for the default distributions and domain constants
+- ...improving the data generation process to be closer to production data
+  (as long as the number of assumptions stays reasonable)
+- ...increasing the quality of the previews and analyses
+- ...reduce technical debt - some features were implemented as they were needed and better patterns exist.
 
 ## Isolating the ConFlowGen development environment
 
diff --git a/Readme.md b/Readme.md
index fff00a19..ba9c9eec 100644
--- a/Readme.md
+++ b/Readme.md
@@ -64,4 +64,4 @@ Examples how to generate synthetic data with ConFlowGen exist
 Use cases for the generated output data are presented
 [in the example usage subdirectory](https://github.com/1kastner/conflowgen/tree/main/example-usage).
 If you wish to contribute to the project, please have a look at
-[Contributing.md](Contributing.md).
+[how to contribute](Contributing.md).
diff --git a/conflowgen/api/container_flow_generation_manager.py b/conflowgen/api/container_flow_generation_manager.py
index 83412b90..619f7413 100644
--- a/conflowgen/api/container_flow_generation_manager.py
+++ b/conflowgen/api/container_flow_generation_manager.py
@@ -1,4 +1,5 @@
 import datetime
+import logging
 from typing import Union, Dict, Optional
 
 from conflowgen.application.repositories.container_flow_generation_properties_repository import \
@@ -18,6 +19,7 @@ class ContainerFlowGenerationManager:
     def __init__(self):
         self.container_flow_generation_service = ContainerFlowGenerationService()
         self.container_flow_generation_properties_repository = ContainerFlowGenerationPropertiesRepository()
+        self.logger = logging.getLogger("conflowgen")
 
     def set_properties(
             self,
@@ -96,7 +98,7 @@ def set_properties(
     def get_properties(self) -> Dict[str, Union[str, datetime.date, float, int]]:
         """
         Returns:
-            The properties of the container flow
+            The properties of the container flow.
         """
         properties = self.container_flow_generation_properties_repository.get_container_flow_generation_properties()
         return {
@@ -118,12 +120,30 @@ def get_properties(self) -> Dict[str, Union[str, datetime.date, float, int]]:
                 properties.maximum_dwell_time_of_transshipment_containers_in_hours
         }
 
-    def generate(self) -> None:
+    def container_flow_data_exists(self) -> bool:
+        """
+        When an existing database is opened, pre-existing container flow data could already be stored inside.
+        Invoking :meth:`.ContainerFlowGenerationManager.generate` again would reset that stored data.
+        You might want to skip that set and just re-use the data already stored in the database.
+
+        Returns:
+            Whether container flow data exists in the database.
+        """
+        return self.container_flow_generation_service.container_flow_data_exists()
+
+    def generate(self, overwrite: bool = True) -> None:
         """
         Generate the synthetic container flow according to all the information stored in the database so far.
         This triggers a multistep procedure of generating vehicles and the containers which are delivered or picked up
         by the vehicles.
         More is described in the Section
         `Data Generation Process <background.rst#data-generation-process>`_.
+        The invocation of this method overwrites any already existent data in the database.
+        Consider checking for
+        :meth:`.ContainerFlowGenerationManager.container_flow_data_exists`
+        and skip invoking this method.
         """
+        if not overwrite and self.container_flow_data_exists():
+            self.logger.debug("Data already exists and it was not asked to overwrite existent data, skip this.")
+            return
         self.container_flow_generation_service.generate()
diff --git a/conflowgen/api/container_length_distribution_manager.py b/conflowgen/api/container_length_distribution_manager.py
index 6a737bf5..a5e2442a 100644
--- a/conflowgen/api/container_length_distribution_manager.py
+++ b/conflowgen/api/container_length_distribution_manager.py
@@ -28,8 +28,8 @@ def set_container_length_distribution(
             container_lengths: Dict[ContainerLength, float]
     ) -> None:
         """
-        Set the assumed global distribution of container lengths. This is applied to all vehicles that arrive at the
-        terminal.
+        Set the assumed global distribution of container lengths.
+        This is applied to all vehicles that arrive at the terminal.
 
         Args:
             container_lengths: The distribution of container lengths and their corresponding frequency.
diff --git a/conflowgen/api/container_storage_requirement_distribution_manager.py b/conflowgen/api/container_storage_requirement_distribution_manager.py
index 7560c47b..42627dc7 100644
--- a/conflowgen/api/container_storage_requirement_distribution_manager.py
+++ b/conflowgen/api/container_storage_requirement_distribution_manager.py
@@ -8,7 +8,8 @@
 
 class ContainerStorageRequirementDistributionManager:
     """
-    This manager provides the interface to set and get the storage requirement distribution.
+    This is the interface to set and get the storage requirement distribution.
+    It determines how many containers are selected to have a certain :class:`.StorageRequirement`.
     """
 
     def __init__(self):
@@ -26,8 +27,8 @@ def set_storage_requirement_distribution(
             storage_requirements: Dict[ContainerLength, Dict[StorageRequirement, float]]
     ) -> None:
         """
-        Set the assumed global distribution of container storage requirements. This is applied to all vehicles that
-        arrive at the terminal.
+        Set the assumed global distribution of container storage requirements.
+        This is applied to all containers passing through the terminal.
 
         Args:
             storage_requirements: The distribution of storage requirements depending on the container length.
diff --git a/conflowgen/api/container_weight_distribution_manager.py b/conflowgen/api/container_weight_distribution_manager.py
index 513b4a83..49a73898 100644
--- a/conflowgen/api/container_weight_distribution_manager.py
+++ b/conflowgen/api/container_weight_distribution_manager.py
@@ -8,8 +8,8 @@
 
 class ContainerWeightDistributionManager:
     """
-    This manager provides the interface to set and get the container weight distribution.
-
+    This is the interface to set and get the container weight distribution.
+    It determines how many containers are selected to have which weight.
     The default distribution is presented in the section
     `Container Weight Distribution <notebooks/input_distributions.ipynb#Container-Weight-Distribution>`_.
     """
diff --git a/conflowgen/api/mode_of_transport_distribution_manager.py b/conflowgen/api/mode_of_transport_distribution_manager.py
index 5a9626c6..1b90f307 100644
--- a/conflowgen/api/mode_of_transport_distribution_manager.py
+++ b/conflowgen/api/mode_of_transport_distribution_manager.py
@@ -7,7 +7,7 @@
 
 class ModeOfTransportDistributionManager:
     """
-    This manager provides the interface to set and get the distribution that controls from which vehicle containers are
+    This is the interface to set and get the distribution that controls from which vehicle containers are
     transshipped to which other type of vehicle.
     """
 
@@ -18,11 +18,10 @@ def get_mode_of_transport_distribution(
             self
     ) -> Dict[ModeOfTransport, Dict[ModeOfTransport, float]]:
         """
-        If a container is delivered by a vehicle of type ``<first-key>``, the floating point number describes the
-        fraction that the container is later picked up by vehicle of type ``<second-key>``.
-
         Returns:
             The distribution of mode of transports dependent on the vehicle the container is delivered by.
+            If a container is delivered by a vehicle of type ``<first-key>``, the floating point number describes the
+            fraction that the container is later picked up by vehicle of type ``<second-key>``.
         """
         return self.mode_of_transport_distribution_repository.get_distribution()
 
diff --git a/conflowgen/flow_generator/container_flow_generation_service.py b/conflowgen/flow_generator/container_flow_generation_service.py
index 793227de..6403dee6 100644
--- a/conflowgen/flow_generator/container_flow_generation_service.py
+++ b/conflowgen/flow_generator/container_flow_generation_service.py
@@ -107,6 +107,10 @@ def clear_previous_container_flow():
 
         Truck.delete().execute()
 
+    @staticmethod
+    def container_flow_data_exists() -> bool:
+        return len(Container.select().limit(1)) == 1
+
     def generate(self):
         self.logger.info("Remove previous data...")
         self.clear_previous_container_flow()
diff --git a/conflowgen/posthoc_analyses/__init__.py b/conflowgen/posthoc_analyses/__init__.py
index 9b2b1e1f..497768e2 100644
--- a/conflowgen/posthoc_analyses/__init__.py
+++ b/conflowgen/posthoc_analyses/__init__.py
@@ -70,4 +70,4 @@ def run_all_analyses(
         "Run all analyses on the synthetically generated data."
     )
     auto_reporter.present_reports(reports)
-    auto_reporter.output.display_explanation("All post-hoc analyses have been run.")
+    auto_reporter.output.display_explanation("All analyses have been run.")
diff --git a/conflowgen/posthoc_analyses/inbound_to_outbound_vehicle_capacity_utilization_analysis_report.py b/conflowgen/posthoc_analyses/inbound_to_outbound_vehicle_capacity_utilization_analysis_report.py
index d8328465..a86021b4 100644
--- a/conflowgen/posthoc_analyses/inbound_to_outbound_vehicle_capacity_utilization_analysis_report.py
+++ b/conflowgen/posthoc_analyses/inbound_to_outbound_vehicle_capacity_utilization_analysis_report.py
@@ -1,11 +1,12 @@
 from __future__ import annotations
 
-from typing import Tuple, Any, Dict
+from typing import Tuple, Any, Dict, Iterable, Optional
 
 import matplotlib.pyplot as plt
 import matplotlib.ticker
 import pandas as pd
 
+from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport
 from conflowgen.posthoc_analyses.inbound_to_outbound_vehicle_capacity_utilization_analysis import \
     InboundToOutboundVehicleCapacityUtilizationAnalysis, CompleteVehicleIdentifier
 from conflowgen.reporting import AbstractReportWithMatplotlib
@@ -27,6 +28,8 @@ class InboundToOutboundVehicleCapacityUtilizationAnalysisReport(AbstractReportWi
 
     maximum_length_for_readable_name = 50  # doc: Each vehicle has a name that might be a bit lengthy for text output
 
+    plot_title = "Capacity utilization analysis"
+
     def __init__(self):
         super().__init__()
         self.analysis = InboundToOutboundVehicleCapacityUtilizationAnalysis(
@@ -54,8 +57,9 @@ def get_report_as_text(self, **kwargs) -> str:
         Returns:
              The report in text format (possibly spanning over several lines).
         """
-        capacities = self._get_capacities_depending_on_vehicle_type(kwargs)
+        vehicle_type, capacities = self._get_capacities_depending_on_vehicle_type(kwargs)
         report = "\n"
+        report += "vehicle type = " + vehicle_type + "\n"
         report += "vehicle identifier                                 "
         report += "inbound capacity (in TEU) "
         report += "outbound capacity (in TEU)"
@@ -77,23 +81,32 @@ def get_report_as_graph(self, **kwargs) -> object:
         The report as a graph is represented as a scatter plot using pandas.
 
         Keyword Args:
-            plot_type: Either "absolute" or "relative".
+            plot_type: Either "absolute", "relative", or "both". Defaults to "both".
             vehicle_type: Either ``"all"``, a single vehicle of type :class:`.ModeOfTransport` or a whole collection of
                 vehicle types, e.g. passed as a :class:`list` or :class:`set`.
                 For the exact interpretation of the parameter, check
                 :class:`.InboundToOutboundVehicleCapacityUtilizationAnalysis`.
 
         Returns:
-             The matplotlib axis of the scatter plot.
+             The matplotlib figure
         """
-        plot_type = kwargs.get("plot_type", "absolute")
+        plot_type = kwargs.get("plot_type", "both")
+
+        vehicle_type, capacities = self._get_capacities_depending_on_vehicle_type(kwargs)
 
-        df = self._convert_analysis_to_df(kwargs)
+        df = self._convert_analysis_to_df(capacities)
 
         if plot_type == "absolute":
-            ax = self._plot_absolute_values(df)
+            fig, ax = plt.subplots(1, 1)
+            self._plot_absolute_values(df, vehicle_type, ax=ax)
         elif plot_type == "relative":
-            ax = self._plot_relative_values(df)
+            fig, ax = plt.subplots(1, 1)
+            self._plot_relative_values(df, vehicle_type, ax=ax)
+        elif plot_type == "both":
+            fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
+            self._plot_absolute_values(df, vehicle_type, ax=ax1)
+            self._plot_relative_values(df, vehicle_type, ax=ax2)
+            plt.subplots_adjust(wspace=0.4)
         else:
             raise Exception(f"Plot type '{plot_type}' is not supported.")
 
@@ -102,35 +115,45 @@ def get_report_as_graph(self, **kwargs) -> object:
             bbox_to_anchor=(1, 0),
             fancybox=True,
         )
-        return ax
-
-    def _plot_absolute_values(self, df: pd.DataFrame) -> matplotlib.pyplot.axis:
-        ax = df.plot.scatter(x="inbound capacity (fixed)", y="used outbound capacity")
+        return fig
+
+    @staticmethod
+    def _get_vehicle_type_representation(vehicle_type: Any) -> str:
+        if vehicle_type is None:
+            return "all"
+        if isinstance(vehicle_type, ModeOfTransport):
+            return str(vehicle_type)
+        if isinstance(vehicle_type, Iterable):
+            return " & ".join([str(element) for element in vehicle_type])
+        return str(vehicle_type)
+
+    def _plot_absolute_values(
+            self, df: pd.DataFrame, vehicle_type: str, ax: Optional[matplotlib.pyplot.axis] = None
+    ) -> matplotlib.pyplot.axis:
+        ax = df.plot.scatter(x="inbound capacity (fixed)", y="used outbound capacity", ax=ax)
         slope = 1 + self.transportation_buffer
         ax.axline((0, 0), slope=slope, color='black', label='Maximum outbound capacity')
         ax.axline((0, 0), slope=1, color='gray', label='Equilibrium')
-        ax.set_title("Inbound to outbound capacity utilization analysis (absolute)")
+        ax.set_title(self.plot_title + " (absolute),\n vehicle type = " + vehicle_type)
         ax.set_aspect('equal', adjustable='box')
         ax.grid(color='lightgray', linestyle=':', linewidth=.5)
         maximum = df[["inbound capacity (fixed)", "used outbound capacity"]].max(axis=1).max(axis=0)
         axis_limitation = maximum * 1.1  # add some white space to the top and left
         ax.set_xlim([0, axis_limitation])
         ax.set_ylim([0, axis_limitation])
-        loc = matplotlib.ticker.MultipleLocator(base=25)
-        ax.xaxis.set_major_locator(loc)
         return ax
 
-    def _plot_relative_values(self, df: pd.DataFrame) -> matplotlib.pyplot.axis:
-        ax = df.plot.scatter(x="inbound capacity (fixed)", y="ratio")
+    def _plot_relative_values(
+            self, df: pd.DataFrame, vehicle_type: str, ax: Optional[matplotlib.pyplot.axis] = None
+    ) -> matplotlib.pyplot.axis:
+        ax = df.plot.scatter(x="inbound capacity (fixed)", y="ratio", ax=ax)
         ax.axline((0, (1 + self.transportation_buffer)), slope=0, color='black', label='Maximum outbound capacity')
         ax.axline((0, 1), slope=0, color='gray', label='Equilibrium')
-        ax.set_title("Inbound to outbound capacity utilization analysis (relative)")
+        ax.set_title(self.plot_title + " (relative),\n vehicle type = " + vehicle_type)
         ax.grid(color='lightgray', linestyle=':', linewidth=.5)
         return ax
 
-    def _convert_analysis_to_df(self, kwargs: dict) -> pd.DataFrame:
-        capacities = self._get_capacities_depending_on_vehicle_type(kwargs)
-
+    def _convert_analysis_to_df(self, capacities: Dict[CompleteVehicleIdentifier, Tuple[float, float]]) -> pd.DataFrame:
         rows = []
         for vehicle_identifier, (inbound_capacity, used_outbound_capacity) in capacities.items():
             vehicle_name = self._create_readable_name(vehicle_identifier)
@@ -143,12 +166,15 @@ def _convert_analysis_to_df(self, kwargs: dict) -> pd.DataFrame:
         df["ratio"] = df["used outbound capacity"] / df["inbound capacity (fixed)"]
         return df
 
-    def _get_capacities_depending_on_vehicle_type(self, kwargs) -> Dict[CompleteVehicleIdentifier, Tuple[float, float]]:
+    def _get_capacities_depending_on_vehicle_type(
+            self, kwargs
+    ) -> Tuple[str, Dict[CompleteVehicleIdentifier, Tuple[float, float]]]:
         if "vehicle_type" in kwargs:
             vehicle_type = kwargs["vehicle_type"]
             capacities = self.analysis.get_inbound_and_outbound_capacity_of_each_vehicle(
                 vehicle_type=vehicle_type
             )
         else:
+            vehicle_type = None
             capacities = self.analysis.get_inbound_and_outbound_capacity_of_each_vehicle()
-        return capacities
+        return self._get_vehicle_type_representation(vehicle_type), capacities
diff --git a/conflowgen/previews/container_flow_by_vehicle_type_preview_report.py b/conflowgen/previews/container_flow_by_vehicle_type_preview_report.py
index ce4e28fa..590e50ed 100644
--- a/conflowgen/previews/container_flow_by_vehicle_type_preview_report.py
+++ b/conflowgen/previews/container_flow_by_vehicle_type_preview_report.py
@@ -17,7 +17,7 @@ class ContainerFlowByVehicleTypePreviewReport(AbstractReportWithPlotly):
     and creates a comprehensible representation for the user, either as text or as a graph.
     The visual and table are expected to approximately look like in the
     `example ContainerFlowByVehicleTypePreviewReport \
-    <notebooks/previews.ipynb#ContainerFlowByVehicleTypePreviewReport>`_.
+    <notebooks/previews.ipynb#Container-Flow-By-Vehicle-Type-Preview-Report>`_.
     """
 
     report_description = """
diff --git a/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview_report.py b/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview_report.py
index b9073e85..28292946 100644
--- a/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview_report.py
+++ b/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview_report.py
@@ -20,7 +20,7 @@ class InboundAndOutboundVehicleCapacityPreviewReport(AbstractReportWithMatplotli
     :class:`.VehicleCapacityExceededPreviewReport`.
     The visual and table are expected to approximately look like in the
     `example InboundAndOutboundVehicleCapacityPreviewReport \
-    <notebooks/previews.ipynb#InboundAndOutboundVehicleCapacityPreviewReport>`_.
+    <notebooks/previews.ipynb#Inbound-And-Outbound-Vehicle-Capacity-Preview-Report>`_.
     """
 
     report_description = """
diff --git a/conflowgen/previews/modal_split_preview_report.py b/conflowgen/previews/modal_split_preview_report.py
index 4be732d3..c99a28c4 100644
--- a/conflowgen/previews/modal_split_preview_report.py
+++ b/conflowgen/previews/modal_split_preview_report.py
@@ -18,7 +18,7 @@ class ModalSplitPreviewReport(AbstractReportWithMatplotlib):
     :class:`.ModalSplitPreview`
     and creates a comprehensible representation for the user, either as text or as a graph.
     The visual and table are expected to approximately look like in the
-    `example ModalSplitPreviewReport <notebooks/previews.ipynb#ModalSplitPreviewReport>`_.
+    `example ModalSplitPreviewReport <notebooks/previews.ipynb#Modal-Split-Preview-Report>`_.
     """
 
     report_description = """
diff --git a/conflowgen/previews/vehicle_capacity_exceeded_preview_report.py b/conflowgen/previews/vehicle_capacity_exceeded_preview_report.py
index 2728fb91..9154ce1e 100644
--- a/conflowgen/previews/vehicle_capacity_exceeded_preview_report.py
+++ b/conflowgen/previews/vehicle_capacity_exceeded_preview_report.py
@@ -19,7 +19,7 @@ class VehicleCapacityExceededPreviewReport(AbstractReportWithMatplotlib):
     :class:`.InboundAndOutboundVehicleCapacityPreviewReport`.
     The visual and table are expected to approximately look like in the
     `example InboundAndOutboundVehicleCapacityPreviewReport \
-    <notebooks/previews.ipynb#InboundAndOutboundVehicleCapacityPreviewReport>`_.
+    <notebooks/previews.ipynb#Inbound-And-Outbound-Vehicle-Capacity-Preview-Report>`_.
     """
 
     report_description = """
diff --git a/conflowgen/reporting/auto_reporter.py b/conflowgen/reporting/auto_reporter.py
index ab34c3be..8b7f547d 100644
--- a/conflowgen/reporting/auto_reporter.py
+++ b/conflowgen/reporting/auto_reporter.py
@@ -33,10 +33,16 @@ def __init__(
 
         self.static_graphs = static_graphs
 
+    @staticmethod
+    def _get_report_name(report_instance: object) -> str:
+        class_name: str = report_instance.__class__.__name__
+        class_name_with_spaces = ''.join(map(lambda x: x if x.islower() else " " + x, class_name))
+        return class_name_with_spaces.strip()
+
     def present_reports(self, reports: Iterable[Type[AbstractReport]]):
         for report in reports:
             report_instance = report()
-            name_of_report = report_instance.__class__.__name__
+            name_of_report = self._get_report_name(report_instance)
             self.output.display_headline(name_of_report)
             self.output.display_explanation(report_instance.report_description)
             if self.as_text:
diff --git a/docs/index.rst b/docs/index.rst
index 4a9824ee..646949b0 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -13,6 +13,7 @@ Its source code hosted
    notebooks/input_distributions.ipynb
    notebooks/minimal_example.ipynb
    notebooks/previews.ipynb
+   notebooks/analyses.ipynb
    example_usage
    api
    contributing
diff --git a/docs/notebooks/analyses.ipynb b/docs/notebooks/analyses.ipynb
new file mode 100644
index 00000000..50341ea0
--- /dev/null
+++ b/docs/notebooks/analyses.ipynb
@@ -0,0 +1,139 @@
+{
+ "cells": [
+  {
+   "cell_type": "raw",
+   "id": "7b6c1c97-b5ba-4e14-9055-c3fb902c72fa",
+   "metadata": {
+    "raw_mimetype": "text/restructuredtext",
+    "tags": []
+   },
+   "source": [
+    "Analyses\n",
+    "--------\n",
+    "\n",
+    "Analyses are run after :meth:`.ContainerFlowGenerationManager.generate`.\n",
+    "An analyses provides first insights on the actually generated data.\n",
+    "They help to answer questions such as:\n",
+    "\n",
+    "#. Was the correct input data used?\n",
+    "   Especially with a lot of input data, it might be tough to keep track of schedules and distributions.\n",
+    "   Before using the generated data, cross-check the generated data with your input data.\n",
+    "   Are the schedules honoured?\n",
+    "   Do the empirical distributions in the generated data match your assumed distributions?\n",
+    "\n",
+    "#. Do the initial assumptions match?\n",
+    "   It is tricky to assign containers to the vehicles on their outbound journey while adhering to distributions and operational constraints at the same time.\n",
+    "   It is worthwhile comparing an analysis with its corresponding preview counterpart (if existent) to detect deviations between the two.\n",
+    "   If they deviate largely and against your personal expectations, maybe the operational constraints need readjustment.\n",
+    "\n",
+    "#. Does the generated data correspond to your expectations?\n",
+    "   Probably you used some of the default distributions and some you researched yourself.\n",
+    "   You might want to cross-check the generated data with external data sources.\n",
+    "   Only that way you can ensure that the data is plausible for the problem you wish to further investigate.\n",
+    "\n",
+    "Please be aware that, as stated in the license, ConFlowGen comes without any warranty.\n",
+    "These analyses are only a first step, so please add your own analyses of the data.\n",
+    "It is up to you to ensure that the generated data actually fits to your task at hand (such as, e.g. a simulation study).\n",
+    "\n",
+    "The steps are explained according to the database created by the\n",
+    "`demo script serving as a Proof of Concept <https://github.com/1kastner/conflowgen/blob/main/demo/demo_poc.py/>`_ ,\n",
+    "but you can still use any other ConFlowGen database."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "35fa184c-5fce-4ae0-a242-687f9fc48d13",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import Markdown\n",
+    "\n",
+    "import conflowgen\n",
+    "\n",
+    "database_chooser = conflowgen.DatabaseChooser(\n",
+    "    sqlite_databases_directory=\"./data/\"\n",
+    ")\n",
+    "database_chooser.load_existing_sqlite_database(\n",
+    "    \"demo_poc.sqlite\"\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f94fa0bb-a7c1-49fd-8999-33b7fc320b76",
+   "metadata": {},
+   "source": [
+    "### Showing Analyses as Text\n",
+    "\n",
+    "For running all analyses, a convencience function exists.\n",
+    "It can simply print all information to the standard output."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7170300b-a7a6-4a34-a8ec-375e4f756f6c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "conflowgen.run_all_analyses(\n",
+    "    as_text=True,\n",
+    "    display_text_func=print,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ce9c1444-aac7-4a13-b177-8177e3008215",
+   "metadata": {},
+   "source": [
+    "### Displaying Analyses as Graphs\n",
+    "\n",
+    "Analyses can also be displayed as graphs.\n",
+    "The depicted information contains the same information but might be easier to grasp.\n",
+    "For emphasis, in the following the text version and graph version of the report are presented side-by-side.\n",
+    "In addition, we also use the Markdown capabilities of the convenience function.\n",
+    "This makes the presented analyses blend into the remaining content."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8a47de90-5708-4bec-b8f4-3d8976e940bb",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "conflowgen.run_all_analyses(\n",
+    "    as_text=True,\n",
+    "    as_graph=True,\n",
+    "    display_text_func=lambda text: display(Markdown(text)),\n",
+    "    display_in_markup_language=\"markdown\",\n",
+    "    static_graphs=True\n",
+    ")"
+   ]
+  }
+ ],
+ "metadata": {
+  "celltoolbar": "Raw Cell Format",
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "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.8.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/docs/notebooks/data/demo_poc.sqlite b/docs/notebooks/data/demo_poc.sqlite
new file mode 100644
index 00000000..ff5f1cd9
--- /dev/null
+++ b/docs/notebooks/data/demo_poc.sqlite
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a742443f44c5dc29f5f7a71e6c7e73fb3f061732cac5573d153ca0d9aca977ec
+size 417792
diff --git a/docs/notebooks/input_distributions.ipynb b/docs/notebooks/input_distributions.ipynb
index 8d2371fd..23c6de3c 100644
--- a/docs/notebooks/input_distributions.ipynb
+++ b/docs/notebooks/input_distributions.ipynb
@@ -29,7 +29,9 @@
    "source": [
     "import datetime\n",
     "import matplotlib.pyplot as plt\n",
-    "import conflowgen"
+    "import conflowgen\n",
+    "import sys\n",
+    "import logging"
    ]
   },
   {
@@ -71,8 +73,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "import sys\n",
-    "import logging\n",
     "logger = logging.getLogger(\"conflowgen\")\n",
     "logger.setLevel(logging.DEBUG)\n",
     "handler = logging.StreamHandler(stream=sys.stdout)\n",
@@ -391,7 +391,7 @@
     "container_flow_generation_manager.set_properties(\n",
     "    start_date=datetime.date(2021, 1, 15),\n",
     "    end_date=datetime.date(2021, 1, 31),\n",
-    "    maximum_dwell_time_of_export_containers_in_hours=10*24\n",
+    "    maximum_dwell_time_of_export_containers_in_hours=10 * 24\n",
     ")\n",
     "container_flow_generation_manager.get_properties()"
    ]
@@ -425,7 +425,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.10"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/docs/notebooks/minimal_example.ipynb b/docs/notebooks/minimal_example.ipynb
index 1d681fa4..23c2e5b6 100644
--- a/docs/notebooks/minimal_example.ipynb
+++ b/docs/notebooks/minimal_example.ipynb
@@ -31,24 +31,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from conflowgen import ContainerFlowGenerationManager\n",
-    "from conflowgen import ModeOfTransport\n",
-    "from conflowgen import PortCallManager\n",
-    "from conflowgen import ExportFileFormat\n",
-    "from conflowgen import ExportContainerFlowManager\n",
-    "from conflowgen import DatabaseChooser\n",
-    "from conflowgen import setup_logger\n",
-    "from conflowgen import InboundAndOutboundVehicleCapacityPreviewReport\n",
-    "from conflowgen import ContainerFlowByVehicleTypePreviewReport\n",
-    "from conflowgen import VehicleCapacityExceededPreviewReport\n",
-    "from conflowgen import ModalSplitPreviewReport\n",
-    "from conflowgen import InboundAndOutboundVehicleCapacityAnalysisReport\n",
-    "from conflowgen import ContainerFlowByVehicleTypeAnalysisReport\n",
-    "from conflowgen import ModalSplitAnalysisReport\n",
-    "from conflowgen import ContainerFlowAdjustmentByVehicleTypeAnalysisReport\n",
-    "from conflowgen import ContainerFlowAdjustmentByVehicleTypeAnalysisSummaryReport\n",
     "import datetime\n",
-    "logger = setup_logger()"
+    "\n",
+    "import conflowgen\n",
+    "\n",
+    "logger = conflowgen.setup_logger()"
    ]
   },
   {
@@ -74,7 +61,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "database_chooser = DatabaseChooser()\n",
+    "database_chooser = conflowgen.DatabaseChooser()\n",
     "demo_file_name = \"my_demo.sqlite\"\n",
     "if demo_file_name in database_chooser.list_all_sqlite_databases():\n",
     "    database_chooser.load_existing_sqlite_database(demo_file_name)\n",
@@ -113,7 +100,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "container_flow_generation_manager = ContainerFlowGenerationManager()\n",
+    "container_flow_generation_manager = conflowgen.ContainerFlowGenerationManager()\n",
     "container_flow_generation_manager.set_properties(\n",
     "    name=\"Demo file\",\n",
     "    start_date=datetime.datetime.now().date(),\n",
@@ -148,7 +135,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "port_call_manager = PortCallManager()"
+    "port_call_manager = conflowgen.PortCallManager()"
    ]
   },
   {
@@ -195,7 +182,7 @@
    "outputs": [],
    "source": [
     "port_call_manager.add_large_scheduled_vehicle(\n",
-    "    vehicle_type=ModeOfTransport.feeder,\n",
+    "    vehicle_type=conflowgen.ModeOfTransport.feeder,\n",
     "    service_name=feeder_service_name,\n",
     "    vehicle_arrives_at=datetime.date(2021, 7, 9),\n",
     "    vehicle_arrives_at_time=datetime.time(11),\n",
@@ -223,12 +210,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "port_call_manager = PortCallManager()\n",
     "feeder_service_name = \"LX050\"\n",
-    "if not port_call_manager.has_schedule(feeder_service_name, vehicle_type=ModeOfTransport.feeder):\n",
+    "if not port_call_manager.has_schedule(feeder_service_name, vehicle_type=conflowgen.ModeOfTransport.feeder):\n",
     "    logger.info(f\"Add feeder service '{feeder_service_name}' to database\")\n",
     "    port_call_manager.add_large_scheduled_vehicle(\n",
-    "        vehicle_type=ModeOfTransport.feeder,\n",
+    "        vehicle_type=conflowgen.ModeOfTransport.feeder,\n",
     "        service_name=feeder_service_name,\n",
     "        vehicle_arrives_at=datetime.date(2021, 7, 9),\n",
     "        vehicle_arrives_at_time=datetime.time(11),\n",
@@ -259,10 +245,10 @@
    "outputs": [],
    "source": [
     "train_service_name = \"JR03A\"\n",
-    "if not port_call_manager.has_schedule(train_service_name, vehicle_type=ModeOfTransport.train):\n",
+    "if not port_call_manager.has_schedule(train_service_name, vehicle_type=conflowgen.ModeOfTransport.train):\n",
     "    logger.info(f\"Add train service '{train_service_name}' to database\")\n",
     "    port_call_manager.add_large_scheduled_vehicle(\n",
-    "        vehicle_type=ModeOfTransport.train,\n",
+    "        vehicle_type=conflowgen.ModeOfTransport.train,\n",
     "        service_name=train_service_name,\n",
     "        vehicle_arrives_at=datetime.date(2021, 7, 12),\n",
     "        vehicle_arrives_at_time=datetime.time(17),\n",
@@ -282,10 +268,10 @@
    "outputs": [],
    "source": [
     "deep_sea_service_name = \"LX050\"\n",
-    "if not port_call_manager.has_schedule(deep_sea_service_name, vehicle_type=ModeOfTransport.deep_sea_vessel):\n",
+    "if not port_call_manager.has_schedule(deep_sea_service_name, vehicle_type=conflowgen.ModeOfTransport.deep_sea_vessel):\n",
     "    logger.info(f\"Add deep sea vessel service '{deep_sea_service_name}' to database\")\n",
     "    port_call_manager.add_large_scheduled_vehicle(\n",
-    "        vehicle_type=ModeOfTransport.deep_sea_vessel,\n",
+    "        vehicle_type=conflowgen.ModeOfTransport.deep_sea_vessel,\n",
     "        service_name=deep_sea_service_name,\n",
     "        vehicle_arrives_at=datetime.date(2021, 7, 10),\n",
     "        vehicle_arrives_at_time=datetime.time(19),\n",
@@ -316,8 +302,8 @@
    },
    "source": [
     "After we have set some exemplary schedules, we can now come to the actual generation.\n",
-    "The :meth:`.ContainerFlowGenerationManager.generate` method starts the generation process of the synthetic\n",
-    "container flow data, based on the information you set earlier."
+    "The :meth:`.ContainerFlowGenerationManager.generate` method starts the generation process of the synthetic container flow data, based on the information you set earlier.\n",
+    "With the optional parameter ``overwrite`` you determine the behavior if already some data exists - with the default behavior ``overwrite=True``, any previous container flow data is overwritten, with ``overwrite=False`` the generation step is skipped if any data is detected."
    ]
   },
   {
@@ -327,7 +313,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "container_flow_generation_manager.generate()"
+    "container_flow_generation_manager.generate(overwrite=False)"
    ]
   }
  ],
@@ -348,7 +334,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.10"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/docs/notebooks/previews.ipynb b/docs/notebooks/previews.ipynb
index cb0f8562..65975101 100644
--- a/docs/notebooks/previews.ipynb
+++ b/docs/notebooks/previews.ipynb
@@ -18,7 +18,7 @@
     "Among others, no container instances are generated and operational constraints are neglected.\n",
     "The steps are explained according to the database created by the\n",
     "`demo script for CTA <https://github.com/1kastner/conflowgen/blob/main/demo/demo_DEHAM_CTA.py/>`_ ,\n",
-    "but you can still use any other conflowgen database."
+    "but you can still use any other ConFlowGen database."
    ]
   },
   {
@@ -28,9 +28,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "import datetime\n",
-    "import matplotlib.pyplot as plt\n",
-    "\n",
     "from IPython.display import Markdown\n",
     "\n",
     "import conflowgen\n",
diff --git a/example-usage/Jupyter Notebook/input data inspection/calibrating the traffic.ipynb b/example-usage/Jupyter Notebook/input data inspection/calibrating the traffic.ipynb
index 616f9e76..3a3fcf03 100644
--- a/example-usage/Jupyter Notebook/input data inspection/calibrating the traffic.ipynb
+++ b/example-usage/Jupyter Notebook/input data inspection/calibrating the traffic.ipynb
@@ -314,9 +314,9 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python [conda env:conflowgen] *",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
-   "name": "conda-env-conflowgen-py"
+   "name": "python3"
   },
   "language_info": {
    "codemirror_mode": {
@@ -328,7 +328,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.12"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/example-usage/Jupyter Notebook/input data inspection/checking input distributions.ipynb b/example-usage/Jupyter Notebook/input data inspection/checking input distributions.ipynb
index 01c06d66..a40257aa 100644
--- a/example-usage/Jupyter Notebook/input data inspection/checking input distributions.ipynb
+++ b/example-usage/Jupyter Notebook/input data inspection/checking input distributions.ipynb
@@ -121,7 +121,7 @@
    "outputs": [],
    "source": [
     "x, y = zip(*list(sorted(weight_distribution[conflowgen.ContainerLength.twenty_feet].items())))\n",
-    "plt.bar(x, [i*100 for i in y])\n",
+    "plt.bar(x, [i * 100 for i in y])\n",
     "\n",
     "# German\n",
     "plt.xlabel(\"Gewicht (in Tonnen)\")\n",
@@ -152,7 +152,7 @@
    "outputs": [],
    "source": [
     "x, y = zip(*list(sorted(weight_distribution[conflowgen.ContainerLength.forty_feet].items())))\n",
-    "plt.bar(x, [i*100 for i in y])\n",
+    "plt.bar(x, [i * 100 for i in y])\n",
     "\n",
     "# German\n",
     "plt.xlabel(\"Gewicht (in Tonnen)\")\n",
@@ -183,7 +183,7 @@
    "outputs": [],
    "source": [
     "hour_in_week, fraction = zip(*list(sorted(truck_arrival_distribution.items())))\n",
-    "weekday_in_week = [x/24 + 1 for x in hour_in_week]\n",
+    "weekday_in_week = [x / 24 + 1 for x in hour_in_week]\n",
     "percentage = [x * 100 for x in fraction]\n",
     "\n",
     "fig, ax = plt.subplots(figsize=(15, 3))\n",
@@ -191,7 +191,7 @@
     "plt.xlim([1, 7])\n",
     "ax.xaxis.grid(True, which='minor', color='gray')\n",
     "ax.xaxis.grid(True, which='major', color='k')\n",
-    "ax.xaxis.set_minor_locator(mticker.MultipleLocator(1/24))\n",
+    "ax.xaxis.set_minor_locator(mticker.MultipleLocator(1 / 24))\n",
     "\n",
     "# German\n",
     "plt.title(\"Ankunftsraten (gemittelt)\")\n",
@@ -292,7 +292,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.12"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/example-usage/Jupyter Notebook/output data inspection/checking container dwell times.ipynb b/example-usage/Jupyter Notebook/output data inspection/checking container dwell times.ipynb
index d072ac17..295d789e 100644
--- a/example-usage/Jupyter Notebook/output data inspection/checking container dwell times.ipynb
+++ b/example-usage/Jupyter Notebook/output data inspection/checking container dwell times.ipynb
@@ -30,8 +30,7 @@
     "import ipywidgets as widgets\n",
     "import pandas as pd\n",
     "from IPython.display import Markdown\n",
-    "import matplotlib.pyplot as plt\n",
-    "from matplotlib import gridspec"
+    "import matplotlib.pyplot as plt"
    ]
   },
   {
@@ -267,7 +266,7 @@
     "y_scaling_factor = 2\n",
     "\n",
     "for i, (name, df) in enumerate(scheduled_vehicle_dfs.items()):\n",
-    "    y_axis.append((i/y_scaling_factor, name))\n",
+    "    y_axis.append((i / y_scaling_factor, name))\n",
     "    if len(df) == 0:\n",
     "        continue\n",
     "    arrivals_and_capacity = df[[\"scheduled_arrival\", \"moved_capacity\"]]\n",
@@ -506,7 +505,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.12"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/example-usage/Jupyter Notebook/output data inspection/checking container properties.ipynb b/example-usage/Jupyter Notebook/output data inspection/checking container properties.ipynb
index 93bd4547..bbe5e5fa 100644
--- a/example-usage/Jupyter Notebook/output data inspection/checking container properties.ipynb
+++ b/example-usage/Jupyter Notebook/output data inspection/checking container properties.ipynb
@@ -134,7 +134,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "df.columns = df.columns.str.replace('_',' ')"
+    "df.columns = df.columns.str.replace('_', ' ')"
    ]
   },
   {
@@ -327,7 +327,7 @@
     "def make_autopct(values):\n",
     "    def my_autopct(pct):\n",
     "        total = sum(values)\n",
-    "        val = int(round(pct*total/100.0))\n",
+    "        val = int(round(pct * total / 100.0))\n",
     "        return '{p:.2f}%\\n({v:d})'.format(p=pct, v=val)\n",
     "    return my_autopct"
    ]
@@ -391,8 +391,8 @@
     "    ][\"emergency pickup\"].value_counts().plot.pie(\n",
     "        label=\"\",\n",
     "        autopct=make_autopct(df_delivered_with_vehicle_type[\n",
-    "        df_delivered_with_vehicle_type[\"picked up by\"] == \"truck\"\n",
-    "    ][\"emergency pickup\"].value_counts().values)\n",
+    "            df_delivered_with_vehicle_type[\"picked up by\"] == \"truck\"\n",
+    "        ][\"emergency pickup\"].value_counts().values)\n",
     "    )\n",
     "    plt.title(\"This amount of containers was picked up by a truck because otherwise \"\n",
     "              \"the maximum dwell time would have been exceeded\")\n",
@@ -439,7 +439,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.12"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/example-usage/Jupyter Notebook/output data inspection/running posthoc analyses.ipynb b/example-usage/Jupyter Notebook/output data inspection/running posthoc analyses.ipynb
deleted file mode 100644
index 842c6fea..00000000
--- a/example-usage/Jupyter Notebook/output data inspection/running posthoc analyses.ipynb
+++ /dev/null
@@ -1,355 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "f74ed45a-9f1c-42dc-8907-958624e0576e",
-   "metadata": {},
-   "source": [
-    "# Running Post-hoc Analyses\n",
-    "\n",
-    "ConFlowGen comes with a set of post-hoc analyses that relate to their preview counterparts.\n",
-    "This means, that e.g.  \n",
-    "`InboundAndOutboundVehicleCapacityPreviewReport`\n",
-    "and\n",
-    "`InboundAndOutboundVehicleCapacityAnalysisReport`\n",
-    "come as a pair.\n",
-    "Based on your input data, first the preview is generated.\n",
-    "In case your operational restrictions (e.g., minimum and maximum container dwell times in combination with the presented schedules) are often not satisfiable, during the container flow generation some of them are relaxed (e.g., the vehicle type for onward transportation).\n",
-    "Thus, you should examine the logs and these visuals\n",
-    "(i.e., the visuals created by `XXXAnalysisReport`)\n",
-    "to ensure that the generated container flows represent the properties you initially desired.\n",
-    "If this is not the case, you need to change the input data according to your needs."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "f36331f9-630a-4b91-afde-39c8f494134f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import matplotlib.pyplot as plt\n",
-    "\n",
-    "from conflowgen import DatabaseChooser\n",
-    "from conflowgen import ContainerFlowGenerationManager\n",
-    "from conflowgen import InboundAndOutboundVehicleCapacityAnalysisReport\n",
-    "from conflowgen import ContainerFlowByVehicleTypeAnalysisReport\n",
-    "from conflowgen import ModalSplitAnalysisReport\n",
-    "from conflowgen import ContainerFlowAdjustmentByVehicleTypeAnalysisReport\n",
-    "from conflowgen import ContainerFlowAdjustmentByVehicleTypeAnalysisSummaryReport\n",
-    "from conflowgen import YardCapacityAnalysisReport\n",
-    "from conflowgen import QuaySideThroughputAnalysisReport\n",
-    "from conflowgen import TruckGateThroughputAnalysisReport\n",
-    "from conflowgen import StorageRequirement"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3feeb2d6-363c-433b-aed3-5f804a465826",
-   "metadata": {},
-   "source": [
-    "## Load database"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "374bb79e-9169-41ad-89c2-c5046aafae01",
-   "metadata": {},
-   "source": [
-    "Load information from database."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "b5ba71c3-d348-4e3c-ab1b-1e97c65a9ce7",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "database_chooser = DatabaseChooser()\n",
-    "demo_file_name = \"demo_deham_cta.sqlite\"\n",
-    "if demo_file_name in database_chooser.list_all_sqlite_databases():\n",
-    "    database_chooser.load_existing_sqlite_database(demo_file_name)\n",
-    "else:\n",
-    "    print(\"Database is missing, nothing to do here\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "2bfd8c80-e412-46df-84e7-74738ddef614",
-   "metadata": {},
-   "source": [
-    "Get information regarding the container flow."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "1f7b3c60-8241-4339-940f-e91be5ff8c62",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "container_flow_generation_manager = ContainerFlowGenerationManager()\n",
-    "container_flow_properties = container_flow_generation_manager.get_properties()\n",
-    "for key, value in container_flow_properties.items():\n",
-    "    print(f\"{key:<60}: {value}\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a01679ae-67eb-4691-a8ad-c44212787b04",
-   "metadata": {},
-   "source": [
-    "## Load text reports\n",
-    "\n",
-    "Generate text reports.\n",
-    "These have been tested with unit tests and serve as guidance for the visualizations."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "db8023b3-9f49-434c-8f61-ac0eab78a15a",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "inbound_and_outbound_vehicle_capacity_analysis_report = InboundAndOutboundVehicleCapacityAnalysisReport()\n",
-    "report = inbound_and_outbound_vehicle_capacity_analysis_report.get_report_as_text()\n",
-    "print(\"Inbound and outbound traffic: \")\n",
-    "print(report)\n",
-    "print()\n",
-    "\n",
-    "container_flow_by_vehicle_type_analysis_report = ContainerFlowByVehicleTypeAnalysisReport()\n",
-    "report = container_flow_by_vehicle_type_analysis_report.get_report_as_text()\n",
-    "print(\"Container flow between vehicle types as generated: \")\n",
-    "print(report)\n",
-    "print()\n",
-    "\n",
-    "modal_split_analysis_report = ModalSplitAnalysisReport()\n",
-    "report = modal_split_analysis_report.get_report_as_text()\n",
-    "print(\"The same container flow expressed in terms of transshipment and modal split for the hinterland: \")\n",
-    "print(report)\n",
-    "print()\n",
-    "\n",
-    "container_flow_adjustment_by_vehicle_type_analysis_report = ContainerFlowAdjustmentByVehicleTypeAnalysisReport()\n",
-    "report = container_flow_adjustment_by_vehicle_type_analysis_report.get_report_as_text()\n",
-    "print(\"Consequences of container flow for outgoing vehicles: \")\n",
-    "print(report)\n",
-    "\n",
-    "container_flow_adjustment_by_vehicle_type_analysis_summary_report = ContainerFlowAdjustmentByVehicleTypeAnalysisSummaryReport()\n",
-    "report = container_flow_adjustment_by_vehicle_type_analysis_summary_report.get_report_as_text()\n",
-    "print(\"The summary of the previous report: \")\n",
-    "print(report)\n",
-    "\n",
-    "yard_capacity_analysis_report = YardCapacityAnalysisReport()\n",
-    "report = yard_capacity_analysis_report.get_report_as_text()\n",
-    "print(\"Analyse the used capacity in the yard\")\n",
-    "print(report)\n",
-    "\n",
-    "quay_side_throughput_analysis_report = QuaySideThroughputAnalysisReport()\n",
-    "report = quay_side_throughput_analysis_report.get_report_as_text()\n",
-    "print(\"Analyse the throughput at the quay side\")\n",
-    "print(report)\n",
-    "\n",
-    "truck_gate_throughput_analysis_report = TruckGateThroughputAnalysisReport()\n",
-    "report = truck_gate_throughput_analysis_report.get_report_as_text()\n",
-    "print(\"Analyse the throughput at the truck gate\")\n",
-    "print(report)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "4c5d1a07-49d5-447b-bbf2-02271aec085d",
-   "metadata": {},
-   "source": [
-    "## Plot inbound and outbound capacities"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "fd950d6d-5f69-4ea5-a99f-f4a6c8341de0",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ax = inbound_and_outbound_vehicle_capacity_analysis_report.get_report_as_graph()\n",
-    "plt.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "098e005e-690f-4b99-acff-ad226e265342",
-   "metadata": {},
-   "source": [
-    "If the actual and maximum capacity are equal, most likely some of the containers needed to be redirected to other vehicle types as not sufficient resources were available."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "ba2dffa4-6d62-4a50-ab83-c9790e4d6ed9",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "fig = container_flow_by_vehicle_type_analysis_report.get_report_as_graph()\n",
-    "fig.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bfa14c82-cf49-44c8-b3d0-3a221145e83f",
-   "metadata": {},
-   "source": [
-    "Please check whether you have expected this container flow as depicted."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "291b0cc4-05ab-4e75-85ae-a0e5dbbdbcd0",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ax = modal_split_analysis_report.get_report_as_graph()\n",
-    "plt.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a128a36f-b704-4ef4-b17f-fec716d12282",
-   "metadata": {},
-   "source": [
-    "This shows the modal split as it is reflected in the actually generated data."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "0793d415-dc43-4b59-b20a-705e5caa0d31",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "fig = container_flow_adjustment_by_vehicle_type_analysis_report.get_report_as_graph()\n",
-    "fig.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f8a66bd9-2af9-42da-878f-2144291832ee",
-   "metadata": {},
-   "source": [
-    "This is the flow from the initially selected vehicle type selected for pick up to the vehicle type that it was changed to in order not to harm the operational constraints.\n",
-    "If the left and right side are the same, this means that nothing has changed."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "21545371-7e36-43f9-8fd5-991cc6cedce5",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ax = container_flow_adjustment_by_vehicle_type_analysis_summary_report.get_report_as_graph()\n",
-    "plt.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ea14c6a5-f05f-4e4e-8177-37ea843d3547",
-   "metadata": {},
-   "source": [
-    "This is a summary of the previously presented flow.\n",
-    "A large fraction of unchanged vehicle types reflects that operational constraints were rarely harmed.\n",
-    "If the fraction of unchanged vehicles is not the majority, some of your assumptions regarding the prevailing container flows through the terminal might require reconsideration."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "ee03f22b-1de2-4a8b-9be2-f0fbf17320d0",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ax = yard_capacity_analysis_report.get_report_as_graph()\n",
-    "plt.show()\n",
-    "ax = yard_capacity_analysis_report.get_report_as_graph(storage_requirement=StorageRequirement.standard)\n",
-    "plt.show()\n",
-    "ax = yard_capacity_analysis_report.get_report_as_graph(storage_requirement={StorageRequirement.reefer, StorageRequirement.dangerous_goods})\n",
-    "plt.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "02cc20e8-d58a-45f7-8c15-2044e6c67196",
-   "metadata": {},
-   "source": [
-    "This shows the used capacity in the yard."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "d81cc901-2124-4d69-81d6-0a16b73fbf2d",
-   "metadata": {},
-   "source": [
-    "Now let's see how many containers cross the quay side each week."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "ea8f78c1-741f-416c-bdf9-784c095806d1",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ax = quay_side_throughput_analysis_report.get_report_as_graph()\n",
-    "plt.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3f0818bc-3e01-4ce9-b6f1-ef11e7c64016",
-   "metadata": {},
-   "source": [
-    "...and how many containers pass the truck gate?"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "a9988472-4882-421d-920b-13767937ec0b",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ax = truck_gate_throughput_analysis_report.get_report_as_graph()\n",
-    "plt.show()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "6b62da6b-596b-4aa4-bcbf-b47cd8a2390e",
-   "metadata": {},
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python [conda env:conflowgen] *",
-   "language": "python",
-   "name": "conda-env-conflowgen-py"
-  },
-  "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.8.12"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/example-usage/Jupyter Notebook/output data inspection/visuals_for_ldic2022_paper.ipynb b/example-usage/Jupyter Notebook/output data inspection/visuals_for_ldic2022_paper.ipynb
index c03f69f0..04c84eb0 100644
--- a/example-usage/Jupyter Notebook/output data inspection/visuals_for_ldic2022_paper.ipynb
+++ b/example-usage/Jupyter Notebook/output data inspection/visuals_for_ldic2022_paper.ipynb
@@ -153,7 +153,7 @@
     "database_chooser = DatabaseChooser()\n",
     "\n",
     "\n",
-    "def initialize_conflowgen(figure_name: str, force_reload=False) -> ContainerFlowGenerationManager:\n",
+    "def initialize_conflowgen(figure_name: str, force_reload: bool = False) -> ContainerFlowGenerationManager:\n",
     "    demo_file_name = f\"demo_deham_cta_visual_{figure_name}.sqlite\"\n",
     "\n",
     "    if demo_file_name in database_chooser.list_all_sqlite_databases():\n",
@@ -363,10 +363,9 @@
     "    }\n",
     "}\n",
     "\n",
-    "\n",
     "mode_of_transport_distribution_manager.set_mode_of_transport_distribution(mode_of_transport_distribution_1a)\n",
     "\n",
-    "container_flow_generation_manager.generate()\n",
+    "container_flow_generation_manager.generate(overwrite=False)\n",
     "export_container_flow_manager = ExportContainerFlowManager()\n",
     "folder_data_visual_1a = \"demo-DEHAM-visual-1a-0-1--\" + str(datetime.datetime.now()).replace(\":\", \"-\").replace(\" \", \"--\").split(\".\")[0]\n",
     "export_container_flow_manager.export(\n",
@@ -431,7 +430,7 @@
     "\n",
     "mode_of_transport_distribution_manager.set_mode_of_transport_distribution(mode_of_transport_distribution_1b)\n",
     "\n",
-    "container_flow_generation_manager.generate()\n",
+    "container_flow_generation_manager.generate(overwrite=False)\n",
     "export_container_flow_manager = ExportContainerFlowManager()\n",
     "folder_data_visual_1b = \"demo-DEHAM-visual-1b-0-1--\" + str(datetime.datetime.now()).replace(\":\", \"-\").replace(\" \", \"--\").split(\".\")[0]\n",
     "export_container_flow_manager.export(\n",
@@ -495,7 +494,7 @@
     "\n",
     "mode_of_transport_distribution_manager.set_mode_of_transport_distribution(mode_of_transport_distribution_1c)\n",
     "\n",
-    "container_flow_generation_manager.generate()\n",
+    "container_flow_generation_manager.generate(overwrite=False)\n",
     "export_container_flow_manager = ExportContainerFlowManager()\n",
     "folder_data_visual_1c = \"demo-DEHAM-visual-1c-0-1--\" + str(datetime.datetime.now()).replace(\":\", \"-\").replace(\" \", \"--\").split(\".\")[0]\n",
     "export_container_flow_manager.export(\n",
@@ -522,7 +521,11 @@
    "cell_type": "code",
    "execution_count": null,
    "id": "f1fe233d-c27d-4685-b617-b22a36021add",
-   "metadata": {},
+   "metadata": {
+    "tags": [
+     "flake8-noqa-cell"
+    ]
+   },
    "outputs": [],
    "source": [
     "if os.name == 'nt':\n",
@@ -850,7 +853,7 @@
     "y_scaling_factor = 2\n",
     "\n",
     "for i, (name, df) in enumerate(scheduled_vehicle_dfs_1b.items()):\n",
-    "    y_axis.append((i/y_scaling_factor, name))\n",
+    "    y_axis.append((i / y_scaling_factor, name))\n",
     "    if len(df) == 0:\n",
     "        continue\n",
     "    for _, row in df.iterrows():\n",
@@ -917,7 +920,7 @@
     "moved_teu_scale_factor = 10\n",
     "scatterplot = ax2.scatter(\n",
     "    x, y,\n",
-    "    s=np.array(z)/moved_teu_scale_factor,\n",
+    "    s=np.array(z) / moved_teu_scale_factor,\n",
     "    marker=\"o\", color=color_capacity_dots, edgecolor=\"k\"\n",
     ")\n",
     "\n",
@@ -1071,7 +1074,7 @@
     "    )\n",
     "    ax.set_title(title)\n",
     "    ax.axline((0, 0), slope=1.2, color='dimgray', label='Ratio of 1:1.2 (20% buffer)')\n",
-    "    ax.axline((0, 0), slope=1,   color='lightgray', label='Ratio of 1:1')\n",
+    "    ax.axline((0, 0), slope=1, color='lightgray', label='Ratio of 1:1')\n",
     "    ax.set_xlim([0, 3500])\n",
     "    ax.set_ylim([0, 3500])\n",
     "    ax.set_aspect('equal', adjustable='box')\n",
@@ -1109,12 +1112,6 @@
     "# just comment this in in case you want to convert the figure from svg to emf\n",
     "convert_to_emf(\"ratio_delivered_and_picked_up_containers_comparison.svg\")"
    ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "4d8dbd00-3fa3-41ac-8273-19796f4130ea",
-   "metadata": {},
-   "source": []
   }
  ],
  "metadata": {
@@ -1133,7 +1130,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.12"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/run_ci_light.bat b/run_ci_light.bat
index 45bade68..b410b2c4 100644
--- a/run_ci_light.bat
+++ b/run_ci_light.bat
@@ -16,7 +16,9 @@ pip3 install -e .[dev,ui] || ECHO.Installation failed && EXIT /B
 
 REM check code quality
 flake8 || ECHO.While linting, flake8 failed && EXIT /B
-pylint conflowgen || ECHO.While linting, pylint failed && EXIT /B
+flake8_nb || ECHO.While linting, flake8_nb failed && EXIT /B
+pylint conflowgen || ECHO.While linting the conflowgen module, pylint failed && EXIT /B
+pylint setup.py || ECHO.While linting setup.py, pylint failed && EXIT /B
 
 REM build docs
 CALL docs/make html || ECHO.Building the documentation failed && EXIT /B
diff --git a/setup.py b/setup.py
index 80bee2db..46e0d4ad 100644
--- a/setup.py
+++ b/setup.py
@@ -43,18 +43,19 @@
             'pytest-github-actions-annotate-failures',  # turns pytest failures into action annotations
 
             # build documentation
-            'sphinx',
-            'sphinx-rtd-theme',
-            'sphinx-toolbox',
-            'myst-parser',
-            'sphinxcontrib-bibtex',
-            'sphinx-math-dollar',
-            'nbsphinx',
-            'jupyterlab',
+            'sphinx',  # build the documentation
+            'sphinx-rtd-theme',  # adding nice sphinx theme
+            'sphinx-toolbox',  # dependency of enum_tools
+            'myst-parser',  # for Contributing.md
+            'sphinxcontrib-bibtex',  # citing...
+            'sphinx-math-dollar',  # allow writing LaTeX code in math mode using dollar symbols
+            'nbsphinx',  # use Jupyter Notebooks
+            'jupyterlab',  # develop the Jupyter Notebooks
 
             # checking code quality
-            'pylint',
-            'flake8',
+            'pylint',  # lint Python code
+            'flake8',  # lint Python code
+            'flake8_nb',  # lint Jupyter Notebooks
 
             # publish at PyPI
             'twine'


Test patch:
diff --git a/conflowgen/tests/api/test_container_flow_generation_manager.py b/conflowgen/tests/api/test_container_flow_generation_manager.py
new file mode 100644
index 00000000..c39d2635
--- /dev/null
+++ b/conflowgen/tests/api/test_container_flow_generation_manager.py
@@ -0,0 +1,113 @@
+import datetime
+import unittest
+import unittest.mock
+
+from conflowgen import ContainerFlowGenerationManager
+from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties
+from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution
+from conflowgen.domain_models.distribution_seeders import mode_of_transport_distribution_seeder
+from conflowgen.domain_models.large_vehicle_schedule import Schedule
+from conflowgen.tests.substitute_peewee_database import setup_sqlite_in_memory_db
+
+
+class TestContainerFlowGenerationManager(unittest.TestCase):
+
+    def setUp(self) -> None:
+        """Create container database in memory"""
+        sqlite_db = setup_sqlite_in_memory_db()
+        sqlite_db.create_tables([
+            ContainerFlowGenerationProperties,
+            ModeOfTransportDistribution,
+            Schedule
+        ])
+        mode_of_transport_distribution_seeder.seed()
+        self.container_flow_generation_manager = ContainerFlowGenerationManager()
+
+    def test_generate_with_overwrite(self):
+        with unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_service,
+                'generate',
+                return_value=None) as mock_method:
+            self.container_flow_generation_manager.generate(overwrite=True)
+        mock_method.assert_called_once()
+
+    def test_generate_without_overwrite_and_no_previous_data(self):
+        with unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_service,
+                'generate',
+                return_value=None) as mock_generate, \
+            unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_service,
+                'container_flow_data_exists',
+                return_value=False) as mock_check:
+            self.container_flow_generation_manager.generate(overwrite=False)
+        mock_check.assert_called_once()
+        mock_generate.assert_called_once()
+
+    def test_generate_without_overwrite_and_some_previous_data(self):
+        with unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_service,
+                'generate',
+                return_value=None) as mock_generate, \
+            unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_service,
+                'container_flow_data_exists',
+                return_value=True) as mock_check:
+            self.container_flow_generation_manager.generate(overwrite=False)
+        mock_check.assert_called_once()
+        mock_generate.assert_not_called()
+
+    def test_get_properties(self):
+
+        class MockedProperties:
+            name = "my test data"
+            start_date = datetime.date(2030, 1, 1)
+            end_date = datetime.date(2030, 12, 31)
+            transportation_buffer = 0.2
+            minimum_dwell_time_of_import_containers_in_hours = 3
+            minimum_dwell_time_of_export_containers_in_hours = 4
+            minimum_dwell_time_of_transshipment_containers_in_hours = 5
+            maximum_dwell_time_of_import_containers_in_hours = 40
+            maximum_dwell_time_of_export_containers_in_hours = 50
+            maximum_dwell_time_of_transshipment_containers_in_hours = 60
+
+        dict_properties = {
+            'name': "my test data",
+            'start_date': datetime.date(2030, 1, 1),
+            'end_date': datetime.date(2030, 12, 31),
+            'transportation_buffer': 0.2,
+            'minimum_dwell_time_of_import_containers_in_hours': 3,
+            'minimum_dwell_time_of_export_containers_in_hours': 4,
+            'minimum_dwell_time_of_transshipment_containers_in_hours': 5,
+            'maximum_dwell_time_of_import_containers_in_hours': 40,
+            'maximum_dwell_time_of_export_containers_in_hours': 50,
+            'maximum_dwell_time_of_transshipment_containers_in_hours': 60
+        }
+
+        with unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_properties_repository,
+                'get_container_flow_generation_properties',
+                return_value=MockedProperties) as mock_method:
+            retrieved_properties = self.container_flow_generation_manager.get_properties()
+        mock_method.assert_called_once()
+        self.assertDictEqual(dict_properties, retrieved_properties)
+
+    def test_set_properties(self):
+        with unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_properties_repository,
+                'set_container_flow_generation_properties',
+                return_value=None) as mock_method:
+            self.container_flow_generation_manager.set_properties(
+                datetime.datetime.now().date(), datetime.datetime.now().date()
+            )
+        properties = ContainerFlowGenerationProperties.get()
+        mock_method.assert_called_once_with(properties)
+
+    def test_container_flow_data_exists(self):
+        with unittest.mock.patch.object(
+                self.container_flow_generation_manager.container_flow_generation_service,
+                'container_flow_data_exists',
+                return_value=True) as mock_method:
+            response = self.container_flow_generation_manager.container_flow_data_exists()
+        mock_method.assert_called_once()
+        self.assertTrue(response)
diff --git a/conflowgen/tests/api/test_container_stream_generation_manager__generate.py b/conflowgen/tests/api/test_container_stream_generation_manager__generate.py
deleted file mode 100644
index 55929726..00000000
--- a/conflowgen/tests/api/test_container_stream_generation_manager__generate.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import unittest
-import unittest.mock
-
-from conflowgen import ContainerFlowGenerationManager
-from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties
-from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution
-from conflowgen.domain_models.distribution_seeders import mode_of_transport_distribution_seeder
-from conflowgen.domain_models.large_vehicle_schedule import Schedule
-from conflowgen.tests.substitute_peewee_database import setup_sqlite_in_memory_db
-
-
-class TestContainerFlowGenerationManager__generate(unittest.TestCase):
-
-    def setUp(self) -> None:
-        """Create container database in memory"""
-        sqlite_db = setup_sqlite_in_memory_db()
-        sqlite_db.create_tables([
-            ContainerFlowGenerationProperties,
-            ModeOfTransportDistribution,
-            Schedule
-        ])
-        mode_of_transport_distribution_seeder.seed()
-        self.container_flow_generation_manager = ContainerFlowGenerationManager()
-
-    def test_generate(self):
-        with unittest.mock.patch.object(
-                self.container_flow_generation_manager.container_flow_generation_service,
-                'generate',
-                return_value=None) as mock_method:
-            self.container_flow_generation_manager.generate()
-        mock_method.assert_called_once()
diff --git a/conflowgen/tests/api/test_container_stream_generation_manager__set_properties.py b/conflowgen/tests/api/test_container_stream_generation_manager__set_properties.py
deleted file mode 100644
index 006f9893..00000000
--- a/conflowgen/tests/api/test_container_stream_generation_manager__set_properties.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import datetime
-import unittest
-import unittest.mock
-
-from conflowgen import ContainerFlowGenerationManager
-from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties
-from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution
-from conflowgen.domain_models.distribution_seeders import mode_of_transport_distribution_seeder
-from conflowgen.domain_models.large_vehicle_schedule import Schedule
-from conflowgen.tests.substitute_peewee_database import setup_sqlite_in_memory_db
-
-
-class TestContainerFlowGenerationManager__set_properties(unittest.TestCase):
-
-    def setUp(self) -> None:
-        """Create container database in memory"""
-        sqlite_db = setup_sqlite_in_memory_db()
-        sqlite_db.create_tables([
-            ContainerFlowGenerationProperties,
-            ModeOfTransportDistribution,
-            Schedule
-        ])
-        mode_of_transport_distribution_seeder.seed()
-        self.container_flow_generation_manager = ContainerFlowGenerationManager()
-
-    def test_set_properties(self):
-        with unittest.mock.patch.object(
-                self.container_flow_generation_manager.container_flow_generation_properties_repository,
-                'set_container_flow_generation_properties',
-                return_value=None) as mock_method:
-            self.container_flow_generation_manager.set_properties(
-                datetime.datetime.now().date(), datetime.datetime.now().date()
-            )
-        properties = ContainerFlowGenerationProperties.get()
-        mock_method.assert_called_once_with(properties)
diff --git a/conflowgen/tests/flow_generator/test_container_flow_generator_service__container_flow_data_exists.py b/conflowgen/tests/flow_generator/test_container_flow_generator_service__container_flow_data_exists.py
new file mode 100644
index 00000000..2501c641
--- /dev/null
+++ b/conflowgen/tests/flow_generator/test_container_flow_generator_service__container_flow_data_exists.py
@@ -0,0 +1,45 @@
+import unittest
+
+from conflowgen import ContainerLength, StorageRequirement
+from conflowgen.domain_models.container import Container
+from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution
+from conflowgen.domain_models.distribution_seeders import mode_of_transport_distribution_seeder
+from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport
+from conflowgen.domain_models.vehicle import LargeScheduledVehicle, Truck
+from conflowgen.flow_generator.container_flow_generation_service import \
+    ContainerFlowGenerationService
+from conflowgen.domain_models.large_vehicle_schedule import Schedule, Destination
+from conflowgen.tests.substitute_peewee_database import setup_sqlite_in_memory_db
+
+
+class TestContainerFlowGeneratorService__generate(unittest.TestCase):
+
+    def setUp(self) -> None:
+        """Create container database in memory"""
+        self.sqlite_db = setup_sqlite_in_memory_db()
+        self.sqlite_db.create_tables([
+            Container,
+            ModeOfTransportDistribution,
+            Schedule,
+            Destination,
+            Truck,
+            LargeScheduledVehicle
+        ])
+        mode_of_transport_distribution_seeder.seed()
+        self.container_Flow_generator_service = ContainerFlowGenerationService()
+
+    def test_container_flow_data_exists_with_no_data(self):
+        container_flow_data_exists = self.container_Flow_generator_service.container_flow_data_exists()
+        self.assertFalse(container_flow_data_exists)
+
+    def test_container_flow_data_exists_with_some_data(self):
+        Container.create(
+            weight=20,
+            length=ContainerLength.twenty_feet,
+            storage_requirement=StorageRequirement.standard,
+            delivered_by=ModeOfTransport.feeder,
+            picked_up_by=ModeOfTransport.truck,
+            picked_up_by_initial=ModeOfTransport.truck
+        )
+        container_flow_data_exists = self.container_Flow_generator_service.container_flow_data_exists()
+        self.assertTrue(container_flow_data_exists)
diff --git a/conflowgen/tests/flow_generator/test_container_stream_generator_service__generate.py b/conflowgen/tests/flow_generator/test_container_flow_generator_service__generate.py
similarity index 100%
rename from conflowgen/tests/flow_generator/test_container_stream_generator_service__generate.py
rename to conflowgen/tests/flow_generator/test_container_flow_generator_service__generate.py
diff --git a/conflowgen/tests/flow_generator/test_export_container_stream_service__container.py b/conflowgen/tests/flow_generator/test_export_container_flow_service__container.py
similarity index 100%
rename from conflowgen/tests/flow_generator/test_export_container_stream_service__container.py
rename to conflowgen/tests/flow_generator/test_export_container_flow_service__container.py
diff --git a/conflowgen/tests/posthoc_analyses/test_container_flow_by_vehicle_type_analysis.py b/conflowgen/tests/posthoc_analyses/test_container_flow_by_vehicle_type_analysis.py
index e3714ac7..7e07c275 100644
--- a/conflowgen/tests/posthoc_analyses/test_container_flow_by_vehicle_type_analysis.py
+++ b/conflowgen/tests/posthoc_analyses/test_container_flow_by_vehicle_type_analysis.py
@@ -51,7 +51,6 @@ def test_with_single_feeder(self):
             average_moved_capacity=300,
             vehicle_arrives_every_k_days=-1
         )
-        schedule.save()
         feeder_lsv = LargeScheduledVehicle.create(
             vehicle_name="TestFeeder1",
             capacity_in_teu=300,
@@ -59,12 +58,10 @@ def test_with_single_feeder(self):
             scheduled_arrival=datetime.datetime.now(),
             schedule=schedule
         )
-        feeder_lsv.save()
-        feeder = Feeder.create(
+        Feeder.create(
             large_scheduled_vehicle=feeder_lsv
         )
-        feeder.save()
-        container = Container.create(
+        Container.create(
             weight=20,
             length=ContainerLength.twenty_feet,
             storage_requirement=StorageRequirement.standard,
@@ -73,7 +70,6 @@ def test_with_single_feeder(self):
             picked_up_by=ModeOfTransport.truck,
             picked_up_by_initial=ModeOfTransport.truck
         )
-        container.save()
 
         inbound_to_outbound_flow = self.analysis.get_inbound_to_outbound_flow()
         self.assertSetEqual(set(ModeOfTransport), set(inbound_to_outbound_flow.keys()))
diff --git a/conflowgen/tests/posthoc_analyses/test_inbound_to_outbound_capacity_utilization_analysis_report.py b/conflowgen/tests/posthoc_analyses/test_inbound_to_outbound_capacity_utilization_analysis_report.py
index 4ed4bacd..9cc32838 100644
--- a/conflowgen/tests/posthoc_analyses/test_inbound_to_outbound_capacity_utilization_analysis_report.py
+++ b/conflowgen/tests/posthoc_analyses/test_inbound_to_outbound_capacity_utilization_analysis_report.py
@@ -40,6 +40,7 @@ def test_with_no_data(self):
         actual_report = self.analysis.get_report_as_text()
         print(actual_report)
         expected_report = """
+vehicle type = all
 vehicle identifier                                 inbound capacity (in TEU) outbound capacity (in TEU)
 --no vehicles exist--
 """
@@ -82,6 +83,7 @@ def test_inbound_with_single_feeder(self):
         actual_report = self.analysis.get_report_as_text()
         print(actual_report)
         expected_report = """
+vehicle type = all
 vehicle identifier                                 inbound capacity (in TEU) outbound capacity (in TEU)
 feeder-TestFeederService-TestFeeder1                                   250.0                        1.0
 (rounding errors might exist)
diff --git a/conflowgen/tests/previews/test_run_all_previews.py b/conflowgen/tests/previews/test_run_all_previews.py
index 750aa93f..592ac8b4 100644
--- a/conflowgen/tests/previews/test_run_all_previews.py
+++ b/conflowgen/tests/previews/test_run_all_previews.py
@@ -34,7 +34,7 @@ def test_with_no_data(self):
         )
         self.assertEqual(
             cm.output[1],
-            "INFO:conflowgen:\nInboundAndOutboundVehicleCapacityPreviewReport\n"
+            "INFO:conflowgen:\nInbound And Outbound Vehicle Capacity Preview Report\n"
         )
         self.assertEqual(
             cm.output[-1],