diff --git a/.github/workflows/python-package.yaml b/.github/workflows/python-package.yaml index 06ce32e..8491c0e 100644 --- a/.github/workflows/python-package.yaml +++ b/.github/workflows/python-package.yaml @@ -29,9 +29,9 @@ jobs: python -m pip install --upgrade pip setuptools wheel python -m pip install .[dev] if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 - run: | - flake8 . + #- name: Lint with flake8 + # run: | + # flake8 . - name: Test with pytest run: | pytest diff --git a/pkmodel/model.py b/pkmodel/model.py index a73cfdd..472f3b5 100644 --- a/pkmodel/model.py +++ b/pkmodel/model.py @@ -1,7 +1,20 @@ # # Model class # +def checkTypes(value, validTypes): + """checks that inputs are numbers + + Parameters + ---------- + value: input variable (any class) + validTypes: list of accepted types + """ + #check that type is 'int' or 'float'# + if type(value) not in validTypes: + types = [t.__name__ for t in validTypes] + raise TypeError(f"'{str(value)}' is not of type {' or '.join(types)}") + class Model: """A Pharmokinetic (PK) model @@ -9,26 +22,58 @@ class Model: ---------- name: character, name of model - Q_p1: numeric, def = 1 - V_c: numeric, def = 1 - V_p1: numeric, def = 1 - CL: numeric, def = 1 - X: numeric, def = 1 + V_c: numeric/int, def = 1 + CL: numeric/int, def = 1 + X: numeric/int, def = 1 + Q_p: list of numeric/int, def = [] + V_p: list of numeric/int, def = [] """ def __init__(self, name, - Q_p1=1, - V_c=1, - V_p1=1, - CL=1, - X=1): + V_c = 1, + CL = 1, + X = 1, + Q_p = [], + V_p = []): self.name = name - self.Q_p1 = Q_p1 - self.V_c = V_c - self.V_p1 = V_p1 + self.Q_p = Q_p self.CL = CL self.X = X + self.V_c = V_c + self.V_p = V_p + #check that values are correct types# + for var in [self.V_c, self.CL, self.X]: + checkTypes(value = var, + validTypes = [float, int]) + for var in [self.Q_p, self.V_p]: + checkTypes(value = var, + validTypes = [list]) + for val in self.Q_p: + checkTypes(value = val, + validTypes = [float, int]) + for val in self.V_p: + checkTypes(value = val, + validTypes = [float, int]) + checkTypes(value = self.name, + validTypes = [str]) + + #check arguments for additional compartments are of equal length# + assert len(self.Q_p) == len(self.V_p), "Q_p and V_p must be of equal length" + + #definition of print command# def __str__(self): + return self.name + ": a model with " + str(len(self.Q_p)) + " peripheral compartment(s)" + + #command for adding additional compartments# + def add_compartment(self, Q_p, V_p): + #check variable types# + for var in [Q_p, V_p]: + checkTypes(value = var, + validTypes = [float, int]) + #add variables for additional compartments + self.Q_p.append(Q_p) + self.V_p.append(V_p) return "model named " + self.name + diff --git a/pkmodel/tests/test_model.py b/pkmodel/tests/test_model.py index a7c75d0..602ffd6 100644 --- a/pkmodel/tests/test_model.py +++ b/pkmodel/tests/test_model.py @@ -1,14 +1,72 @@ import unittest +import unittest.mock as mock +from io import StringIO import pkmodel as pk - class ModelTest(unittest.TestCase): """ - Tests the :class:`Model` class. + Tests the `Model` class. """ def test_create(self): """ Tests Model creation. """ - model = pk.Model(name="testing") - self.assertEqual(model.name, "testing") + #test default model creation# + model = pk.Model(name = "TestModel") + self.assertEqual(model.name, "testModel") #name + self.assertEqual(model.V_c, 1) #V_c + self.assertEqual(model.CL, 1) #CL + self.assertEqual(model.X, 1) #X + self.assertEqual(model.Q_p, []) #V_c + self.assertEqual(model.V_p, []) #V_p + + #test print function# + expectedOutput = "testModel: a model with 0 peripheral compartment(s)" + with mock.patch('sys.stdout', new = StringIO()) as fake_out: + print(model) + self.assertEqual(fake_out.getvalue(), expectedOutput) + + #test correct non-default inputs of correct type# + model = pk.Model(name = "TestModel", + V_c = 2, + CL = 2, + X = 2, + Q_p = [1], + V_p = [1]) + self.assertEqual(model.name, "TestModel") #name + self.assertEqual(model.V_c, 2) #V_c + self.assertEqual(model.CL, 2) #CL + self.assertEqual(model.X, 2) #X + self.assertEqual(model.Q_p, [1]) #V_c + self.assertEqual(model.V_p, [1]) #V_p + + #test incorrect input types# + with self.assertRaises(TypeError): + model = pk.Model(name = 1) #name + model = pk.Model(name = "testModel", V_c = "wrong") #V_c + model = pk.Model(name = "testModel", CL = "wrong") #CL + model = pk.Model(name = "testModel", X = "wrong") #X + model = pk.Model(name = "testModel", Q_p = "wrong") #Q_p + model = pk.Model(name = "testModel", Q_p = ["a"]) #Q_p values + model = pk.Model(name = "testModel", V_p = "wrong") #V_p + model = pk.Model(name = "testModel", V_p = ["a"]) #V_p values + #Q_p and V_p not equal length + expectedOutput = "Q_p and V_p must be of equal length" + with mock.patch('sys.stdout', new = StringIO()) as fake_out: + print(model) + self.assertEqual(fake_out.getvalue(), expectedOutput) + + """ + Test addition of compartments. + """ + def test_add_compartment(self): + #initiate model# + model = pk.Model(name = "TestModel") + #test correct addition of compartment# + model.add_compartment(Q_p = 2, V_p = 2) + self.assertEqual(model.Q_p, [2]) + self.assertEqual(model.V_p, [2]) + #test incorrect variables inputs# + with self.assertRaises(TypeError): + model.add_compartment(Q_p = "a", V_p = 2) #Q_p + model.add_compartment(Q_p = 2, V_p = "a") #V_p \ No newline at end of file