Repository: AI-SDC/SACRO-ML (language: python)

Issue:
package for pypi



Discussion / hints:

Fix patch:
diff --git a/.codecov.yml b/.codecov.yml
index ce553fcf..b09d15da 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,4 +1,6 @@
-
-
+---
+# configuration for https://codecov.io
 ignore:
-  - "safemodel/classifiers/new_model_template.py"
+  - "setup.py"
+  - "aisdc/safemodel/classifiers/new_model_template.py"
+...
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 9451bfb0..0ec80c08 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -3,6 +3,8 @@ name: lint
 
 on:
   push:
+    paths-ignore:
+      - '**.md'
 
 jobs:
   build:
@@ -20,5 +22,5 @@ jobs:
 
       - name: pylint
         run: |
-          pylint -v --recursive=True safemodel attacks tests --fail-under 10
+          pylint -v --recursive=True aisdc.safemodel aisdc.attacks tests --fail-under 10
 ...
diff --git a/.github/workflows/sphinx-docs.yml b/.github/workflows/sphinx-docs.yml
index 419d12f2..e7414560 100644
--- a/.github/workflows/sphinx-docs.yml
+++ b/.github/workflows/sphinx-docs.yml
@@ -5,6 +5,8 @@ on:
   push:
     branches:
       - main
+    paths-ignore:
+      - '**.md'
 
 jobs:
   build:
diff --git a/CITATION.cff b/CITATION.cff
index 62207156..11ee3d51 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -1,8 +1,8 @@
 cff-version: 1.2.0
 title: AI-SDC
-version: 1.0.0
-doi: 10.5281/zenodo.7080280
-date-released: 2022-09-14
+version: 1.0.1
+doi:
+date-released: 2022-11-16
 license: MIT
 repository-code: https://github.com/AI-SDC/AI-SDC
 languages:
@@ -27,8 +27,8 @@ authors:
     given-names: Simon
     orcid: https://orcid.org/0000-0003-3578-4477
     affiliation: NHS National Services Scotland
-  - family-names: Boixader
-    given-names: Alba Crespi
+  - family-names: Boixader Crespi
+    given-names: Alba
     affiliation: University of Dundee
   - family-names: Liley
     given-names: James
diff --git a/README.md b/README.md
index bdd82d85..f87571b0 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
 [![DOI](https://zenodo.org/badge/518801511.svg)](https://zenodo.org/badge/latestdoi/518801511)
 [![Codacy Badge](https://app.codacy.com/project/badge/Grade/63d31eeb27ec445f9fa9c5866d8bec0e)](https://www.codacy.com/gh/AI-SDC/AI-SDC/dashboard)
 [![codecov](https://codecov.io/gh/AI-SDC/AI-SDC/branch/development/graph/badge.svg?token=AXX2XCXUNU)](https://codecov.io/gh/AI-SDC/AI-SDC)
+[![PyPI package](https://img.shields.io/pypi/v/aisdc.svg)](https://pypi.org/project/aisdc)
+[![Python versions](https://img.shields.io/pypi/pyversions/aisdc.svg)](https://pypi.org/project/aisdc)
 
 # AI-SDC
 
@@ -10,19 +12,20 @@ A collection of tools and resources for managing the statistical disclosure cont
 
 ## Content
 
-* `attacks` : Contains a variety of privacy attacks on machine learning models, including membership and attribute inference.
-* `docs` : Contains Sphinx documentation files.
-* `example_notebooks` : Contains short tutorials on the basic concept of "safe_XX" versions of machine learning algorithms, and examples of some specific algorithms.
-* `examples` : Contains examples of how to run the code contained in this repository:
+* `aisdc`
+    - `attacks` Contains a variety of privacy attacks on machine learning models, including membership and attribute inference.
+    - `preprocessing` Contains preprocessing modules for test datasets.
+    - `safemodel` The safemodel package is an open source wrapper for common machine learning models. It is designed for use by researchers in Trusted Research Environments (TREs) where disclosure control methods must be implemented. Safemodel aims to give researchers greater confidence that their models are more compliant with disclosure control.
+* `docs` Contains Sphinx documentation files.
+* `example_notebooks` Contains short tutorials on the basic concept of "safe_XX" versions of machine learning algorithms, and examples of some specific algorithms.
+* `examples` Contains examples of how to run the code contained in this repository:
   - How to simulate attribute inference attacks `attribute_inference_example.py`.
   - How to simulate membership inference attacks:
     + Worst case scenario attack `worst_case_attack_example.py`.
     + LIRA scenario attack `lira_attack_example.py`.
   - Integration of attacks into safemodel classes `safemodel_attack_integration_bothcalls.py`.
-* `preprocessing` : Contains preprocessing modules for test datasets.
-* `risk_examples` : Contains hypothetical examples of data leakage through machine learning models as described in the [Green Paper](https://doi.org/10.5281/zenodo.6896214).
-* `safemodel` : The safemodel package is an open source wrapper for common machine learning models. It is designed for use by researchers in Trusted Research Environments (TREs) where disclosure control methods must be implemented. Safemodel aims to give researchers greater confidence that their models are more compliant with disclosure control.
-* `tests` : Contains unit tests.
+* `risk_examples` Contains hypothetical examples of data leakage through machine learning models as described in the [Green Paper](https://doi.org/10.5281/zenodo.6896214).
+* `tests` Contains unit tests.
 
 ## Documentation
 
diff --git a/attacks/__init__.py b/aisdc/__init__.py
similarity index 100%
rename from attacks/__init__.py
rename to aisdc/__init__.py
diff --git a/safemodel/README.md b/aisdc/attacks/__init__.py
similarity index 100%
rename from safemodel/README.md
rename to aisdc/attacks/__init__.py
diff --git a/attacks/attack.py b/aisdc/attacks/attack.py
similarity index 90%
rename from attacks/attack.py
rename to aisdc/attacks/attack.py
index 7beb0630..ab40af2b 100644
--- a/attacks/attack.py
+++ b/aisdc/attacks/attack.py
@@ -2,7 +2,7 @@
 
 import sklearn
 
-from attacks.dataset import Data
+from aisdc.attacks.dataset import Data
 
 
 class Attack:
diff --git a/attacks/attribute_attack.py b/aisdc/attacks/attribute_attack.py
similarity index 99%
rename from attacks/attribute_attack.py
rename to aisdc/attacks/attribute_attack.py
index d9738410..55cafbc0 100644
--- a/attacks/attribute_attack.py
+++ b/aisdc/attacks/attribute_attack.py
@@ -17,9 +17,9 @@
 from sklearn.base import BaseEstimator
 from sklearn.preprocessing import OneHotEncoder
 
-from attacks import report
-from attacks.attack import Attack
-from attacks.dataset import Data
+from aisdc.attacks import report
+from aisdc.attacks.attack import Attack
+from aisdc.attacks.dataset import Data
 
 logging.basicConfig(level=logging.INFO)
 logger = logging.getLogger("aia")
diff --git a/attacks/dataset.py b/aisdc/attacks/dataset.py
similarity index 100%
rename from attacks/dataset.py
rename to aisdc/attacks/dataset.py
diff --git a/attacks/likelihood_attack.py b/aisdc/attacks/likelihood_attack.py
similarity index 99%
rename from attacks/likelihood_attack.py
rename to aisdc/attacks/likelihood_attack.py
index 16a14a15..b407806d 100644
--- a/attacks/likelihood_attack.py
+++ b/aisdc/attacks/likelihood_attack.py
@@ -17,9 +17,9 @@
 from sklearn.ensemble import RandomForestClassifier
 from sklearn.model_selection import train_test_split
 
-from attacks import metrics, report
-from attacks.attack import Attack
-from attacks.dataset import Data
+from aisdc.attacks import metrics, report
+from aisdc.attacks.attack import Attack
+from aisdc.attacks.dataset import Data
 
 logging.basicConfig(level=logging.INFO)
 
diff --git a/attacks/metrics.py b/aisdc/attacks/metrics.py
similarity index 100%
rename from attacks/metrics.py
rename to aisdc/attacks/metrics.py
diff --git a/attacks/mia_extremecase.py b/aisdc/attacks/mia_extremecase.py
similarity index 100%
rename from attacks/mia_extremecase.py
rename to aisdc/attacks/mia_extremecase.py
diff --git a/attacks/report.py b/aisdc/attacks/report.py
similarity index 100%
rename from attacks/report.py
rename to aisdc/attacks/report.py
diff --git a/attacks/worst_case_attack.py b/aisdc/attacks/worst_case_attack.py
similarity index 99%
rename from attacks/worst_case_attack.py
rename to aisdc/attacks/worst_case_attack.py
index 21b8afef..24fb3640 100644
--- a/attacks/worst_case_attack.py
+++ b/aisdc/attacks/worst_case_attack.py
@@ -14,9 +14,9 @@
 from sklearn.ensemble import RandomForestClassifier
 from sklearn.model_selection import train_test_split
 
-from attacks import metrics, report
-from attacks.attack import Attack
-from attacks.dataset import Data
+from aisdc.attacks import metrics, report
+from aisdc.attacks.attack import Attack
+from aisdc.attacks.dataset import Data
 
 logging.basicConfig(level=logging.INFO)
 
diff --git a/aisdc/preprocessing/__init__.py b/aisdc/preprocessing/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/preprocessing/loaders.py b/aisdc/preprocessing/loaders.py
similarity index 100%
rename from preprocessing/loaders.py
rename to aisdc/preprocessing/loaders.py
diff --git a/aisdc/safemodel/README.md b/aisdc/safemodel/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/safemodel/__init__.py b/aisdc/safemodel/__init__.py
similarity index 70%
rename from safemodel/__init__.py
rename to aisdc/safemodel/__init__.py
index 098baade..065492e1 100644
--- a/safemodel/__init__.py
+++ b/aisdc/safemodel/__init__.py
@@ -1,2 +1,2 @@
-""" empty file"""
+"""empty file"""
 from .reporting import get_reporting_string
diff --git a/safemodel/classifiers/__init__.py b/aisdc/safemodel/classifiers/__init__.py
similarity index 100%
rename from safemodel/classifiers/__init__.py
rename to aisdc/safemodel/classifiers/__init__.py
diff --git a/safemodel/classifiers/dp_svc.py b/aisdc/safemodel/classifiers/dp_svc.py
similarity index 100%
rename from safemodel/classifiers/dp_svc.py
rename to aisdc/safemodel/classifiers/dp_svc.py
diff --git a/safemodel/classifiers/new_model_template.py b/aisdc/safemodel/classifiers/new_model_template.py
similarity index 100%
rename from safemodel/classifiers/new_model_template.py
rename to aisdc/safemodel/classifiers/new_model_template.py
diff --git a/safemodel/classifiers/safedecisiontreeclassifier.py b/aisdc/safemodel/classifiers/safedecisiontreeclassifier.py
similarity index 100%
rename from safemodel/classifiers/safedecisiontreeclassifier.py
rename to aisdc/safemodel/classifiers/safedecisiontreeclassifier.py
diff --git a/safemodel/classifiers/safekeras.py b/aisdc/safemodel/classifiers/safekeras.py
similarity index 99%
rename from safemodel/classifiers/safekeras.py
rename to aisdc/safemodel/classifiers/safekeras.py
index 44595ca4..f2f598a4 100644
--- a/safemodel/classifiers/safekeras.py
+++ b/aisdc/safemodel/classifiers/safekeras.py
@@ -20,10 +20,9 @@
 from tensorflow.keras import Model as KerasModel  # pylint: disable = import-error
 from tensorflow_privacy.privacy.analysis import compute_dp_sgd_privacy
 
-from safemodel.reporting import get_reporting_string
-
 # safemodel superclass
-from safemodel.safemodel import SafeModel
+from ..reporting import get_reporting_string
+from ..safemodel import SafeModel
 
 # suppress numerous deprecatino warnings
 # shut tensorflow up
diff --git a/safemodel/classifiers/saferandomforestclassifier.py b/aisdc/safemodel/classifiers/saferandomforestclassifier.py
similarity index 100%
rename from safemodel/classifiers/saferandomforestclassifier.py
rename to aisdc/safemodel/classifiers/saferandomforestclassifier.py
diff --git a/safemodel/classifiers/safesvc.py b/aisdc/safemodel/classifiers/safesvc.py
similarity index 100%
rename from safemodel/classifiers/safesvc.py
rename to aisdc/safemodel/classifiers/safesvc.py
diff --git a/safemodel/classifiers/safetf.py b/aisdc/safemodel/classifiers/safetf.py
similarity index 96%
rename from safemodel/classifiers/safetf.py
rename to aisdc/safemodel/classifiers/safetf.py
index a38845e1..902e115d 100644
--- a/safemodel/classifiers/safetf.py
+++ b/aisdc/safemodel/classifiers/safetf.py
@@ -15,7 +15,7 @@
 from tensorflow_privacy.privacy.analysis import compute_dp_sgd_privacy
 from tensorflow_privacy.privacy.optimizers import dp_optimizer_keras
 
-from safemodel.safemodel import SafeModel
+from ..safemodel import SafeModel
 
 
 class Safe_tf_DPModel(SafeModel, DPModel):
diff --git a/safemodel/reporting.py b/aisdc/safemodel/reporting.py
similarity index 100%
rename from safemodel/reporting.py
rename to aisdc/safemodel/reporting.py
diff --git a/safemodel/rules.json b/aisdc/safemodel/rules.json
similarity index 100%
rename from safemodel/rules.json
rename to aisdc/safemodel/rules.json
diff --git a/safemodel/safemodel.py b/aisdc/safemodel/safemodel.py
similarity index 99%
rename from safemodel/safemodel.py
rename to aisdc/safemodel/safemodel.py
index 9f3ab43d..5f3715cd 100644
--- a/safemodel/safemodel.py
+++ b/aisdc/safemodel/safemodel.py
@@ -18,8 +18,8 @@
 # import tensorflow as tf
 from dictdiffer import diff
 
-from attacks import attribute_attack, dataset, report, worst_case_attack
-from attacks.likelihood_attack import (  # pylint: disable = import-error
+from aisdc.attacks import attribute_attack, dataset, report, worst_case_attack
+from aisdc.attacks.likelihood_attack import (  # pylint: disable = import-error
     LIRAAttack,
     LIRAAttackArgs,
 )
diff --git a/docs/source/attacks.rst b/docs/source/attacks.rst
index f6d0cc17..2732136d 100644
--- a/docs/source/attacks.rst
+++ b/docs/source/attacks.rst
@@ -3,13 +3,13 @@ Attacks
 
 Examples showing how to run the code can be found in the examples folder.
 
-.. automodule:: attacks.metrics
+.. automodule:: aisdc.attacks.metrics
     :members:
-.. automodule:: attacks.mia_extremecase
+.. automodule:: aisdc.attacks.mia_extremecase
     :members:
-.. automodule:: attacks.worst_case_attack
+.. automodule:: aisdc.attacks.worst_case_attack
     :members:
-.. automodule:: attacks.report
+.. automodule:: aisdc.attacks.report
     :members:
-.. automodule:: attacks.likelihood_attack
+.. automodule:: aisdc.attacks.likelihood_attack
     :members:
diff --git a/docs/source/preprocessing.rst b/docs/source/preprocessing.rst
index 4a561ddb..6ad9794e 100644
--- a/docs/source/preprocessing.rst
+++ b/docs/source/preprocessing.rst
@@ -3,5 +3,5 @@ Preprocessing
 ================
 
 
-.. automodule:: preprocessing.loaders
+.. automodule:: aisdc.preprocessing.loaders
    :members:
diff --git a/docs/source/safedecisiontree.rst b/docs/source/safedecisiontree.rst
index 98b0d94a..6bf82ef1 100644
--- a/docs/source/safedecisiontree.rst
+++ b/docs/source/safedecisiontree.rst
@@ -3,5 +3,5 @@ SafeDecisionTree
 
 An example Python Notebook is available  `Here <https://github.com/jim-smith/GRAIMatter/blob/main/WP2/wrapper/wrapper-concept.ipynb>`_
 
-.. automodule:: safemodel.classifiers.safedecisiontreeclassifier
+.. automodule:: aisdc.safemodel.classifiers.safedecisiontreeclassifier
    :members:
diff --git a/docs/source/safekeras.rst b/docs/source/safekeras.rst
index d653ea8f..5b6aa7f5 100644
--- a/docs/source/safekeras.rst
+++ b/docs/source/safekeras.rst
@@ -3,5 +3,5 @@ SafeKerasModel
 
 An example Python Notebook is available  `Here <https://github.com/jim-smith/GRAIMatter/blob/main/WP2/wrapper/wrapper-concept.ipynb>`_
 
-.. automodule:: safemodel.classifiers.safekeras
+.. automodule:: aisdc.safemodel.classifiers.safekeras
    :members:
diff --git a/docs/source/safemodel.rst b/docs/source/safemodel.rst
index ff4b6ea3..74c6eda8 100644
--- a/docs/source/safemodel.rst
+++ b/docs/source/safemodel.rst
@@ -1,7 +1,7 @@
 Base Class
 ==========
 
-.. automodule:: safemodel.safemodel
+.. automodule:: aisdc.safemodel.safemodel
    :members:
    :special-members:
    :private-members:
diff --git a/docs/source/saferandomforest.rst b/docs/source/saferandomforest.rst
index d81664cc..8390a108 100644
--- a/docs/source/saferandomforest.rst
+++ b/docs/source/saferandomforest.rst
@@ -5,5 +5,5 @@ SafeRandomForest
 An example Python Notebook is available  `Here <https://github.com/jim-smith/GRAIMatter/blob/main/WP2/wrapper/wrapper-RF-concept.ipynb>`_
 
 
-.. automodule:: safemodel.classifiers.saferandomforestclassifier
+.. automodule:: aisdc.safemodel.classifiers.saferandomforestclassifier
    :members:
diff --git a/example_notebooks/readme.md b/example_notebooks/README.md
similarity index 91%
rename from example_notebooks/readme.md
rename to example_notebooks/README.md
index f8123662..239b3c85 100644
--- a/example_notebooks/readme.md
+++ b/example_notebooks/README.md
@@ -2,7 +2,7 @@
 
 This directory contains some example python notebooks.
 We hope they are constructive to help you understand the
-core concepts and functionanlity provided by safe models.
+core concepts and functionality provided by safe models.
 
 The main notebook is the example-notebook-safedecisiontree which can be considered
 as a tutorial that talks through the different concepts and functionality provided
diff --git a/example_notebooks/example-notebook-SVC.ipynb b/example_notebooks/example-notebook-SVC.ipynb
index 0daaa64b..8bb08e6a 100644
--- a/example_notebooks/example-notebook-SVC.ipynb
+++ b/example_notebooks/example-notebook-SVC.ipynb
@@ -46,7 +46,7 @@
     "logger.setLevel(logging.INFO)\n",
     "# ROOT_PROJECT_FOLDER = os.path.dirname(os.path.dirname(__file__))\n",
     "# sys.path.append(ROOT_PROJECT_FOLDER)\n",
-    "from safemodel.classifiers import SafeSVC"
+    "from aisdc.safemodel.classifiers import SafeSVC"
    ]
   },
   {
diff --git a/example_notebooks/example-notebook-decisiontree.ipynb b/example_notebooks/example-notebook-decisiontree.ipynb
index 7c751dd4..64bea0a6 100644
--- a/example_notebooks/example-notebook-decisiontree.ipynb
+++ b/example_notebooks/example-notebook-decisiontree.ipynb
@@ -240,8 +240,8 @@
     }
    ],
    "source": [
-    "from safemodel.safemodel import SafeModel\n",
-    "from safemodel.classifiers import SafeDecisionTreeClassifier"
+    "from aisdc.safemodel.safemodel import SafeModel\n",
+    "from aisdc.safemodel.classifiers import SafeDecisionTreeClassifier"
    ]
   },
   {
diff --git a/example_notebooks/example-notebook-keras.ipynb b/example_notebooks/example-notebook-keras.ipynb
index 7ec622d0..b774a320 100644
--- a/example_notebooks/example-notebook-keras.ipynb
+++ b/example_notebooks/example-notebook-keras.ipynb
@@ -59,7 +59,7 @@
     "from sklearn.neural_network import MLPClassifier\n",
     "\n",
     "# Safe Keras\n",
-    "from safemodel.classifiers import SafeKerasModel\n",
+    "from aisdc.safemodel.classifiers import SafeKerasModel\n",
     "\n",
     "# set tensorflow messages to warning level\n",
     "tf.get_logger().setLevel(\"WARNING\")"
@@ -588,7 +588,7 @@
     }
    ],
    "source": [
-    "from safemodel.classifiers.safekeras import load_safe_keras_model\n",
+    "from aisdc.safemodel.classifiers.safekeras import load_safe_keras_model\n",
     "\n",
     "reloaded_model = load_safe_keras_model(\"safe1.tf\")"
    ]
diff --git a/example_notebooks/example-notebook-randomforest.ipynb b/example_notebooks/example-notebook-randomforest.ipynb
index 7eded277..a51d7a7c 100644
--- a/example_notebooks/example-notebook-randomforest.ipynb
+++ b/example_notebooks/example-notebook-randomforest.ipynb
@@ -118,8 +118,8 @@
     }
    ],
    "source": [
-    "from safemodel.safemodel import SafeModel\n",
-    "from safemodel.classifiers import SafeRandomForestClassifier"
+    "from aisdc.safemodel.safemodel import SafeModel\n",
+    "from aisdc.safemodel.classifiers import SafeRandomForestClassifier"
    ]
   },
   {
@@ -259,8 +259,8 @@
     }
    ],
    "source": [
-    "from safemodel.safemodel import SafeModel\n",
-    "from safemodel.classifiers import SafeRandomForestClassifier\n",
+    "from aisdc.safemodel.safemodel import SafeModel\n",
+    "from aisdc.safemodel.classifiers import SafeRandomForestClassifier\n",
     "\n",
     "safeRFModel = SafeRandomForestClassifier(n_estimators=100)  # (criterion=\"entropy\")\n",
     "safeRFModel.fit(X, y)\n",
diff --git a/examples/attribute_inference_example.py b/examples/attribute_inference_example.py
index 03adff20..c7442078 100644
--- a/examples/attribute_inference_example.py
+++ b/examples/attribute_inference_example.py
@@ -15,7 +15,7 @@
 from sklearn.model_selection import train_test_split
 from sklearn.preprocessing import LabelEncoder, OneHotEncoder
 
-from attacks import attribute_attack, dataset  # pylint: disable = import-error
+from aisdc.attacks import attribute_attack, dataset  # pylint: disable = import-error
 
 # pylint: disable = duplicate-code
 
diff --git a/examples/lira_attack_example.py b/examples/lira_attack_example.py
index f4a93bcc..2517a26a 100644
--- a/examples/lira_attack_example.py
+++ b/examples/lira_attack_example.py
@@ -40,8 +40,8 @@
 from sklearn.ensemble import RandomForestClassifier
 from sklearn.model_selection import train_test_split
 
-from attacks.dataset import Data  # pylint: disable = import-error
-from attacks.likelihood_attack import (  # pylint: disable = import-error
+from aisdc.attacks.dataset import Data  # pylint: disable = import-error
+from aisdc.attacks.likelihood_attack import (  # pylint: disable = import-error
     LIRAAttack,
     LIRAAttackArgs,
 )
diff --git a/examples/safemodel_attack_integration_bothcalls.py b/examples/safemodel_attack_integration_bothcalls.py
index bc9ea45e..cbf0a44f 100644
--- a/examples/safemodel_attack_integration_bothcalls.py
+++ b/examples/safemodel_attack_integration_bothcalls.py
@@ -10,8 +10,8 @@
 from sklearn.model_selection import train_test_split
 from sklearn.preprocessing import LabelEncoder, OneHotEncoder
 
-from attacks.dataset import Data
-from safemodel.classifiers import SafeDecisionTreeClassifier
+from aisdc.attacks.dataset import Data
+from aisdc.safemodel.classifiers import SafeDecisionTreeClassifier
 
 if __name__ == "__main__":
 
diff --git a/examples/worst_case_attack_example.py b/examples/worst_case_attack_example.py
index 43cdb617..4c56705e 100644
--- a/examples/worst_case_attack_example.py
+++ b/examples/worst_case_attack_example.py
@@ -27,7 +27,7 @@
 from sklearn.model_selection import train_test_split
 from sklearn.svm import SVC
 
-from attacks import dataset, worst_case_attack  # pylint: disable = import-error
+from aisdc.attacks import dataset, worst_case_attack  # pylint: disable = import-error
 
 # [Researcher] Access a dataset
 X, y = load_breast_cancer(return_X_y=True, as_frame=False)
diff --git a/risk_examples/readme.md b/risk_examples/README.md
similarity index 100%
rename from risk_examples/readme.md
rename to risk_examples/README.md
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..2f9e7b9a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,56 @@
+"""Python setup script for installing AI-SDC."""
+
+from pathlib import Path
+
+from setuptools import find_packages, setup
+
+this_directory = Path(__file__).parent
+long_description = (this_directory / "README.md").read_text()
+
+setup(
+    name="aisdc",
+    version="1.0.1",
+    license="MIT",
+    maintainer="Jim Smith",
+    maintainer_email="james.smith@uwe.ac.uk",
+    description="",
+    long_description=long_description,
+    long_description_content_type="text/markdown",
+    url="https://github.com/AI-SDC/AI-SDC",
+    packages=find_packages(exclude=["tests"]),
+    package_data={"aisdc.safemodel": ["rules.json"]},
+    python_requires=">=3.10",
+    install_requires=[
+        "dictdiffer==0.9.0",
+        "fpdf==1.7.2",
+        "joblib==1.1.0",
+        "multiprocess==0.70.12.2",
+        "numpy==1.21.5",
+        "pandas==1.5.0",
+        "scikit_learn==1.1.2",
+        "scipy==1.7.2",
+        "tensorflow==2.10.0",
+        "tensorflow_privacy==0.8.5",
+    ],
+    classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "Intended Audience :: Developers",
+        "Intended Audience :: Science/Research",
+        "License :: OSI Approved :: MIT License",
+        "Natural Language :: English",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3.11",
+        "Topic :: Scientific/Engineering",
+        "Topic :: Scientific/Engineering :: Artificial Intelligence",
+        "Topic :: Scientific/Engineering :: Information Analysis",
+        "Operating System :: OS Independent",
+    ],
+    keywords=[
+        "data-privacy",
+        "data-protection",
+        "machine-learning",
+        "privacy",
+        "privacy-tools",
+        "statistical-disclosure-control",
+    ],
+)


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 96f839cb..80c9a856 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -3,6 +3,8 @@ name: test
 
 on:
   push:
+    paths-ignore:
+      - '**.md'
 
 jobs:
   build:
diff --git a/tests/datasets/nursery_as_dataset.pickle b/tests/datasets/nursery_as_dataset.pickle
index 83999070..34d85c8a 100644
Binary files a/tests/datasets/nursery_as_dataset.pickle and b/tests/datasets/nursery_as_dataset.pickle differ
diff --git a/tests/test_attacks.py b/tests/test_attacks.py
index 4752749c..6b5e3e9e 100644
--- a/tests/test_attacks.py
+++ b/tests/test_attacks.py
@@ -8,8 +8,8 @@
 import pytest
 from fpdf import FPDF
 
-from attacks import attack, dataset, mia_extremecase, report
-from safemodel.classifiers import SafeDecisionTreeClassifier
+from aisdc.attacks import attack, dataset, mia_extremecase, report
+from aisdc.safemodel.classifiers import SafeDecisionTreeClassifier
 
 BORDER = 0
 
diff --git a/tests/test_attacks_dataset.py b/tests/test_attacks_dataset.py
index bd012c82..4b881671 100644
--- a/tests/test_attacks_dataset.py
+++ b/tests/test_attacks_dataset.py
@@ -6,7 +6,7 @@
 from sklearn.model_selection import train_test_split
 from sklearn.preprocessing import LabelEncoder, OneHotEncoder
 
-from attacks.dataset import Data
+from aisdc.attacks.dataset import Data
 
 
 def test_dataset():  # pylint:disable=too-many-locals
diff --git a/tests/test_attacks_via_safemodel.py b/tests/test_attacks_via_safemodel.py
index 085c3660..c98622c1 100644
--- a/tests/test_attacks_via_safemodel.py
+++ b/tests/test_attacks_via_safemodel.py
@@ -12,9 +12,9 @@
 from sklearn.model_selection import train_test_split
 from sklearn.preprocessing import LabelEncoder, OneHotEncoder
 
-from attacks import attribute_attack, likelihood_attack, worst_case_attack
-from attacks.dataset import Data
-from safemodel.classifiers import SafeDecisionTreeClassifier
+from aisdc.attacks import attribute_attack, likelihood_attack, worst_case_attack
+from aisdc.attacks.dataset import Data
+from aisdc.safemodel.classifiers import SafeDecisionTreeClassifier
 
 # pylint: disable=too-many-locals,bare-except,duplicate-code,unnecessary-dunder-call
 
@@ -32,7 +32,7 @@ def get_nursery_dataset() -> Data:
 
     """
 
-    the_file = "tests/datasets/nursery_as_dataset.pickle"
+    the_file = os.path.normpath("tests/datasets/nursery_as_dataset.pickle")
     save_locally = True
     need_download = True
 
@@ -96,15 +96,17 @@ def get_nursery_dataset() -> Data:
         y_test = label_enc.transform(y_test_orig)
 
         # add dummy continuous valued attribute from N(0.5,0.05)
-        dummy_tr = 0.5 + 0.05 * np.random.randn(x_train.shape[0], 1)
-        dummy_te = 0.5 + 0.05 * np.random.randn(x_test.shape[0], 1)
-        dummy_all = np.hstack((dummy_tr, dummy_te))
+        dummy_tr = 0.5 + 0.05 * np.random.randn(x_train.shape[0])
+        dummy_te = 0.5 + 0.05 * np.random.randn(x_test.shape[0])
+        dummy_all = np.hstack((dummy_tr, dummy_te)).reshape(-1, 1)
+        dummy_tr = dummy_tr.reshape(-1, 1)
+        dummy_te = dummy_te.reshape(-1, 1)
 
         x_train = np.hstack((x_train, dummy_tr))
         x_train_orig = np.hstack((x_train_orig, dummy_tr))
         x_test = np.hstack((x_test, dummy_te))
         x_test_orig = np.hstack((x_test_orig, dummy_te))
-        xmore = np.hstack((x, dummy_all))
+        xmore = np.concatenate((x_train_orig, x_test_orig))
         n_features = np.shape(x_train_orig)[1]
 
         # [TRE / Researcher] Wrap the data in a dataset object
diff --git a/tests/test_attribute_inference_attack.py b/tests/test_attribute_inference_attack.py
index d16ed06d..f46429a9 100644
--- a/tests/test_attribute_inference_attack.py
+++ b/tests/test_attribute_inference_attack.py
@@ -18,8 +18,12 @@
     OneHotEncoder,
 )
 
-from attacks import attribute_attack  # pylint: disable = import-error
-from attacks.attribute_attack import _get_bounds_risk, _infer_categorical, _unique_max
+from aisdc.attacks import attribute_attack  # pylint: disable = import-error
+from aisdc.attacks.attribute_attack import (
+    _get_bounds_risk,
+    _infer_categorical,
+    _unique_max,
+)
 from tests.test_attacks_via_safemodel import get_nursery_dataset
 
 # pylint: disable = duplicate-code
diff --git a/tests/test_data_interface.py b/tests/test_data_interface.py
index 378ee12e..834f3a1b 100644
--- a/tests/test_data_interface.py
+++ b/tests/test_data_interface.py
@@ -6,7 +6,7 @@
 
 import pandas as pd
 
-from preprocessing.loaders import (  # pylint: disable = import-error
+from aisdc.preprocessing.loaders import (  # pylint: disable = import-error
     UnknownDataset,
     get_data_sklearn,
 )
diff --git a/tests/test_lira_attack.py b/tests/test_lira_attack.py
index 36ce0eb8..753171a2 100644
--- a/tests/test_lira_attack.py
+++ b/tests/test_lira_attack.py
@@ -14,9 +14,9 @@
 from sklearn.ensemble import RandomForestClassifier
 from sklearn.model_selection import train_test_split
 
-from attacks import likelihood_attack
-from attacks.dataset import Data  # pylint: disable = import-error
-from attacks.likelihood_attack import (  # pylint: disable = import-error
+from aisdc.attacks import likelihood_attack
+from aisdc.attacks.dataset import Data  # pylint: disable = import-error
+from aisdc.attacks.likelihood_attack import (  # pylint: disable = import-error
     LIRAAttack,
     LIRAAttackArgs,
 )
diff --git a/tests/test_loaders.py b/tests/test_loaders.py
index 9b269553..739fce44 100644
--- a/tests/test_loaders.py
+++ b/tests/test_loaders.py
@@ -10,8 +10,8 @@
 
 import pytest
 
-from preprocessing import loaders
-from preprocessing.loaders import DataNotAvailable, UnknownDataset
+from aisdc.preprocessing import loaders
+from aisdc.preprocessing.loaders import DataNotAvailable, UnknownDataset
 
 PROJECT_ROOT_FOLDER = os.path.dirname(os.path.dirname(__file__))
 DATA_FOLDER = os.path.join(PROJECT_ROOT_FOLDER, "tests", "datasets")
diff --git a/tests/test_metrics.py b/tests/test_metrics.py
index e0ccca03..9d0c6333 100644
--- a/tests/test_metrics.py
+++ b/tests/test_metrics.py
@@ -6,8 +6,8 @@
 
 import numpy as np
 
-from attacks.metrics import _div, _tpr_at_fpr, get_metrics
-from attacks.mia_extremecase import min_max_disc
+from aisdc.attacks.metrics import _div, _tpr_at_fpr, get_metrics
+from aisdc.attacks.mia_extremecase import min_max_disc
 
 # pylint: disable = invalid-name
 
diff --git a/tests/test_safedecisiontreeclassifier.py b/tests/test_safedecisiontreeclassifier.py
index 0d8ab497..f04dbb21 100644
--- a/tests/test_safedecisiontreeclassifier.py
+++ b/tests/test_safedecisiontreeclassifier.py
@@ -7,9 +7,9 @@
 import numpy as np
 from sklearn import datasets
 
-from safemodel import reporting
-from safemodel.classifiers import SafeDecisionTreeClassifier
-from safemodel.classifiers.safedecisiontreeclassifier import (
+from aisdc.safemodel import reporting
+from aisdc.safemodel.classifiers import SafeDecisionTreeClassifier
+from aisdc.safemodel.classifiers.safedecisiontreeclassifier import (
     decision_trees_are_equal,
     get_tree_k_anonymity,
 )
diff --git a/tests/test_safekeras.old b/tests/test_safekeras.old
index 9ac9cd8b..3078ecb9 100644
--- a/tests/test_safekeras.old
+++ b/tests/test_safekeras.old
@@ -14,7 +14,7 @@ from tensorflow.keras.layers import (  # pylint: disable = import-error, no-name
     Input,
 )
 
-from safemodel.classifiers import SafeKerasModel, safekeras
+from aisdc.safemodel.classifiers import SafeKerasModel, safekeras
 
 EPOCHS = 1
 n_classes = 4
diff --git a/tests/test_safekeras2.py b/tests/test_safekeras2.py
index b32d62ee..f5dbe783 100644
--- a/tests/test_safekeras2.py
+++ b/tests/test_safekeras2.py
@@ -17,8 +17,8 @@
     Input,
 )
 
-from safemodel.classifiers import SafeKerasModel, safekeras
-from safemodel.reporting import get_reporting_string
+from aisdc.safemodel.classifiers import SafeKerasModel, safekeras
+from aisdc.safemodel.reporting import get_reporting_string
 
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 warnings.filterwarnings("ignore", category=FutureWarning)
diff --git a/tests/test_safemodel.py b/tests/test_safemodel.py
index 14dd4e99..7fa67ea9 100644
--- a/tests/test_safemodel.py
+++ b/tests/test_safemodel.py
@@ -7,8 +7,8 @@
 import numpy as np
 from sklearn import datasets
 
-from safemodel.reporting import get_reporting_string
-from safemodel.safemodel import SafeModel
+from aisdc.safemodel.reporting import get_reporting_string
+from aisdc.safemodel.safemodel import SafeModel
 
 notok_start = get_reporting_string(name="warn_possible_disclosure_risk")
 ok_start = get_reporting_string(name="within_recommended_ranges")
diff --git a/tests/test_saferandomforestclassifier.py b/tests/test_saferandomforestclassifier.py
index 3a93af7d..f766f76f 100644
--- a/tests/test_saferandomforestclassifier.py
+++ b/tests/test_saferandomforestclassifier.py
@@ -8,8 +8,8 @@
 import numpy as np
 from sklearn import datasets
 
-from safemodel.classifiers import SafeRandomForestClassifier
-from safemodel.reporting import get_reporting_string
+from aisdc.safemodel.classifiers import SafeRandomForestClassifier
+from aisdc.safemodel.reporting import get_reporting_string
 
 EXPECTED_ACC = 0.9470198675496688  # 5 estimators, min_samples_leaf 5
 
diff --git a/tests/test_safesvc.py b/tests/test_safesvc.py
index 6bbdcf80..a64188c5 100644
--- a/tests/test_safesvc.py
+++ b/tests/test_safesvc.py
@@ -9,7 +9,7 @@
 from sklearn.linear_model import LogisticRegression
 from sklearn.svm import SVC
 
-from safemodel.classifiers import SafeSVC
+from aisdc.safemodel.classifiers import SafeSVC
 
 
 def get_data():
diff --git a/tests/test_safetf.py b/tests/test_safetf.py
index 69c02d1e..2dd16672 100644
--- a/tests/test_safetf.py
+++ b/tests/test_safetf.py
@@ -4,7 +4,7 @@
 
 import pytest
 
-from safemodel.classifiers import safetf
+from aisdc.safemodel.classifiers import safetf
 
 
 def test_Safe_tf_DPModel_l2_and_noise():
diff --git a/tests/test_worst_case_attack.py b/tests/test_worst_case_attack.py
index b039d276..a85c851f 100644
--- a/tests/test_worst_case_attack.py
+++ b/tests/test_worst_case_attack.py
@@ -10,7 +10,7 @@
 from sklearn.model_selection import train_test_split
 from sklearn.svm import SVC
 
-from attacks import dataset, worst_case_attack  # pylint: disable = import-error
+from aisdc.attacks import dataset, worst_case_attack  # pylint: disable = import-error
 
 
 def clean_up(name):