From ca218c18d926c7d6c2ca275eec9125475f9e82a3 Mon Sep 17 00:00:00 2001 From: Anshuman Laskar Date: Sun, 6 Oct 2024 14:05:29 +0530 Subject: [PATCH 1/6] Overflow and Type Errors added to concept.py . Required tests for quantity class added in tests. --- .python-version | 1 + healthchain/models/data/concept.py | 18 +++++++++++++- poetry.lock | 4 +-- pyproject.toml | 1 + tests/test_quantity_class.py | 39 ++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 .python-version create mode 100644 tests/test_quantity_class.py diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..09dcc78 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.11 diff --git a/healthchain/models/data/concept.py b/healthchain/models/data/concept.py index 0ef4d7d..e163733 100644 --- a/healthchain/models/data/concept.py +++ b/healthchain/models/data/concept.py @@ -1,5 +1,5 @@ from enum import Enum -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from typing import Optional, Dict, Union @@ -20,6 +20,22 @@ class Quantity(DataType): # TODO: validate conversions str <-> float value: Optional[Union[str, float]] = None unit: Optional[str] = None + + @field_validator('value') + @classmethod + def validate_value(cls, value:Union[str,float]): + if value is None: + raise TypeError(f"Value CANNOT be a {type(value)} object. Must be float or string in float format.") + + try : + return float(value) + + except ValueError : + raise ValueError(f"Invalid value '{value}' . Must be a float Number.") + + except OverflowError: + raise OverflowError(f"Invalid value . Value is too large resulting in overflow.") + class Range(DataType): diff --git a/poetry.lock b/poetry.lock index bd41e59..7d58932 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -3752,4 +3752,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12" -content-hash = "9c31e43a846ad145298179fd2e01eb47e2a824a86857b4d69d7f12ccb8123f6d" +content-hash = "92e7107fce8f07eeea30ef29cfc758f0ef206e796208b6060335c338040868fd" diff --git a/pyproject.toml b/pyproject.toml index 99f7a96..81c0932 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ termcolor = "^2.4.0" spyne = "^2.14.0" lxml = "^5.2.2" xmltodict = "^0.13.0" +pytest = "^8.3.3" [tool.poetry.group.dev.dependencies] ruff = "^0.4.2" diff --git a/tests/test_quantity_class.py b/tests/test_quantity_class.py new file mode 100644 index 0000000..023d843 --- /dev/null +++ b/tests/test_quantity_class.py @@ -0,0 +1,39 @@ +import pytest +from healthchain.models.data.concept import Quantity +from pydantic import ValidationError +#Valid Cases +def test_valid_float_and_integer(): + valid_floats = [1.0, .1, 4., 5.99999, 12455.321, 33, 1234] + for num in valid_floats : + q = Quantity(value = num,unit = "mg"); + assert q.value == num + +def test_valid_string(): + valid_strings = ["100","100.000001",".1","1.",".123","1234.","123989"] + for string in valid_strings: + q = Quantity(value = string,unit = "mg"); + assert q.value == float(string) + +# Invalid Cases +def test_invalid_strings(): + invalid_strings = ["1.0.0", "1..123", "..123","12..","12a.56","1e4.6","12#.45","12.12@3","12@3"] + for string in invalid_strings: + with pytest.raises(ValidationError) as exception_info: + q = Quantity(value = string,unit = "mg") + assert "Invalid value" in str(exception_info.value) + + +#Edge Cases +def test_edge_cases(): + + edge_cases = ["", "None", None] + for val in edge_cases: + with pytest.raises((ValidationError,TypeError)) as exception_info: + q = Quantity(value = val,unit = "mg") + + exception_info_str = str(exception_info.value) + assert any(msg in exception_info_str for msg in ["CANNOT", "Invalid value"]) + +# if __name__ == '__main__': +# q = Quantity("12","mg"); +# print(q); \ No newline at end of file From ad78f8de8bdb3287e25f3457954bf5cb33752336 Mon Sep 17 00:00:00 2001 From: Anshuman Laskar Date: Thu, 10 Oct 2024 03:14:35 +0530 Subject: [PATCH 2/6] minor issues fixed --- .gitignore | 1 + poetry.lock | 2 +- pyproject.toml | 1 - tests/test_quantity_class.py | 6 +----- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index d168a8c..aaa3416 100644 --- a/.gitignore +++ b/.gitignore @@ -164,3 +164,4 @@ scrap/ .DS_Store .vscode/ .ruff_cache/ +.python-version diff --git a/poetry.lock b/poetry.lock index 7d58932..f653cf9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3752,4 +3752,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12" -content-hash = "92e7107fce8f07eeea30ef29cfc758f0ef206e796208b6060335c338040868fd" +content-hash = "9c31e43a846ad145298179fd2e01eb47e2a824a86857b4d69d7f12ccb8123f6d" diff --git a/pyproject.toml b/pyproject.toml index 81c0932..99f7a96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ termcolor = "^2.4.0" spyne = "^2.14.0" lxml = "^5.2.2" xmltodict = "^0.13.0" -pytest = "^8.3.3" [tool.poetry.group.dev.dependencies] ruff = "^0.4.2" diff --git a/tests/test_quantity_class.py b/tests/test_quantity_class.py index 023d843..481f59f 100644 --- a/tests/test_quantity_class.py +++ b/tests/test_quantity_class.py @@ -32,8 +32,4 @@ def test_edge_cases(): q = Quantity(value = val,unit = "mg") exception_info_str = str(exception_info.value) - assert any(msg in exception_info_str for msg in ["CANNOT", "Invalid value"]) - -# if __name__ == '__main__': -# q = Quantity("12","mg"); -# print(q); \ No newline at end of file + assert any(msg in exception_info_str for msg in ["CANNOT", "Invalid value"]) \ No newline at end of file From fb1209ace938d5554cace6627007305e98c0d837 Mon Sep 17 00:00:00 2001 From: Anshuman Laskar Date: Thu, 10 Oct 2024 03:24:26 +0530 Subject: [PATCH 3/6] code formatted --- .github/pull_request_template.md | 7 ++--- healthchain/models/data/concept.py | 23 ++++++++------- tests/test_quantity_class.py | 45 +++++++++++++++++++----------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bc323ae..84fa182 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,9 +7,9 @@ ## Changes Made -- -- -- +- +- +- ## Testing @@ -27,4 +27,3 @@ ## Additional Notes - diff --git a/healthchain/models/data/concept.py b/healthchain/models/data/concept.py index e163733..fb560ae 100644 --- a/healthchain/models/data/concept.py +++ b/healthchain/models/data/concept.py @@ -20,22 +20,25 @@ class Quantity(DataType): # TODO: validate conversions str <-> float value: Optional[Union[str, float]] = None unit: Optional[str] = None - - @field_validator('value') + + @field_validator("value") @classmethod - def validate_value(cls, value:Union[str,float]): + def validate_value(cls, value: Union[str, float]): if value is None: - raise TypeError(f"Value CANNOT be a {type(value)} object. Must be float or string in float format.") - - try : + raise TypeError( + f"Value CANNOT be a {type(value)} object. Must be float or string in float format." + ) + + try: return float(value) - - except ValueError : + + except ValueError: raise ValueError(f"Invalid value '{value}' . Must be a float Number.") except OverflowError: - raise OverflowError(f"Invalid value . Value is too large resulting in overflow.") - + raise OverflowError( + "Invalid value . Value is too large resulting in overflow." + ) class Range(DataType): diff --git a/tests/test_quantity_class.py b/tests/test_quantity_class.py index 481f59f..375542d 100644 --- a/tests/test_quantity_class.py +++ b/tests/test_quantity_class.py @@ -1,35 +1,48 @@ -import pytest +import pytest from healthchain.models.data.concept import Quantity from pydantic import ValidationError -#Valid Cases + + +# Valid Cases def test_valid_float_and_integer(): - valid_floats = [1.0, .1, 4., 5.99999, 12455.321, 33, 1234] - for num in valid_floats : - q = Quantity(value = num,unit = "mg"); + valid_floats = [1.0, 0.1, 4.0, 5.99999, 12455.321, 33, 1234] + for num in valid_floats: + q = Quantity(value=num, unit="mg") assert q.value == num + def test_valid_string(): - valid_strings = ["100","100.000001",".1","1.",".123","1234.","123989"] + valid_strings = ["100", "100.000001", ".1", "1.", ".123", "1234.", "123989"] for string in valid_strings: - q = Quantity(value = string,unit = "mg"); + q = Quantity(value=string, unit="mg") assert q.value == float(string) + # Invalid Cases def test_invalid_strings(): - invalid_strings = ["1.0.0", "1..123", "..123","12..","12a.56","1e4.6","12#.45","12.12@3","12@3"] + invalid_strings = [ + "1.0.0", + "1..123", + "..123", + "12..", + "12a.56", + "1e4.6", + "12#.45", + "12.12@3", + "12@3", + ] for string in invalid_strings: with pytest.raises(ValidationError) as exception_info: - q = Quantity(value = string,unit = "mg") + Quantity(value=string, unit="mg") assert "Invalid value" in str(exception_info.value) - -#Edge Cases + +# Edge Cases def test_edge_cases(): - edge_cases = ["", "None", None] for val in edge_cases: - with pytest.raises((ValidationError,TypeError)) as exception_info: - q = Quantity(value = val,unit = "mg") - + with pytest.raises((ValidationError, TypeError)) as exception_info: + Quantity(value=val, unit="mg") + exception_info_str = str(exception_info.value) - assert any(msg in exception_info_str for msg in ["CANNOT", "Invalid value"]) \ No newline at end of file + assert any(msg in exception_info_str for msg in ["CANNOT", "Invalid value"]) From 0270c036738b6466bc4b76f1867c315f71fd51de Mon Sep 17 00:00:00 2001 From: Anshuman Laskar Date: Thu, 10 Oct 2024 03:31:49 +0530 Subject: [PATCH 4/6] python-version file removed --- .python-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .python-version diff --git a/.python-version b/.python-version deleted file mode 100644 index 09dcc78..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.10.11 From 918551321869243b671c2b7c1aff359fb9976c80 Mon Sep 17 00:00:00 2001 From: Anshuman Laskar Date: Fri, 11 Oct 2024 13:59:31 +0530 Subject: [PATCH 5/6] Removed TypeError for None and updated tests --- healthchain/models/data/concept.py | 3 +++ tests/test_quantity_class.py | 18 +++++------------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/healthchain/models/data/concept.py b/healthchain/models/data/concept.py index fb560ae..f6b859c 100644 --- a/healthchain/models/data/concept.py +++ b/healthchain/models/data/concept.py @@ -25,6 +25,9 @@ class Quantity(DataType): @classmethod def validate_value(cls, value: Union[str, float]): if value is None: + return None + + if not isinstance(value, (str, float)): raise TypeError( f"Value CANNOT be a {type(value)} object. Must be float or string in float format." ) diff --git a/tests/test_quantity_class.py b/tests/test_quantity_class.py index 375542d..87b9f01 100644 --- a/tests/test_quantity_class.py +++ b/tests/test_quantity_class.py @@ -4,8 +4,8 @@ # Valid Cases -def test_valid_float_and_integer(): - valid_floats = [1.0, 0.1, 4.0, 5.99999, 12455.321, 33, 1234] +def test_valid(): + valid_floats = [1.0, 0.1, 4.0, 5.99999, 12455.321, 33, 1234, None] for num in valid_floats: q = Quantity(value=num, unit="mg") assert q.value == num @@ -30,19 +30,11 @@ def test_invalid_strings(): "12#.45", "12.12@3", "12@3", + "abc", + "None", + "", ] for string in invalid_strings: with pytest.raises(ValidationError) as exception_info: Quantity(value=string, unit="mg") assert "Invalid value" in str(exception_info.value) - - -# Edge Cases -def test_edge_cases(): - edge_cases = ["", "None", None] - for val in edge_cases: - with pytest.raises((ValidationError, TypeError)) as exception_info: - Quantity(value=val, unit="mg") - - exception_info_str = str(exception_info.value) - assert any(msg in exception_info_str for msg in ["CANNOT", "Invalid value"]) From 59ede20edb0ddae99359fdc4a0f46e2ad0e4a9e0 Mon Sep 17 00:00:00 2001 From: Anshuman Laskar Date: Fri, 11 Oct 2024 20:34:18 +0530 Subject: [PATCH 6/6] test_cdaannotator updated --- tests/test_cdaannotator.py | 4 ++-- tests/test_quantity_class.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cdaannotator.py b/tests/test_cdaannotator.py index dd1562e..86e4d85 100644 --- a/tests/test_cdaannotator.py +++ b/tests/test_cdaannotator.py @@ -68,12 +68,12 @@ def test_extract_medications(cda_annotator): assert medications[0].route.code_system_name == "NCI Thesaurus" assert medications[0].route.display_name == "Oral" - assert medications[0].frequency.period.value == ".5" + assert medications[0].frequency.period.value == 0.5 assert medications[0].frequency.period.unit == "d" assert medications[0].frequency.institution_specified assert medications[0].duration.low is None - assert medications[0].duration.high.value == "20221020" + assert medications[0].duration.high.value == 20221020 assert medications[0].precondition == { "@typeCode": "PRCN", diff --git a/tests/test_quantity_class.py b/tests/test_quantity_class.py index 87b9f01..1413fd5 100644 --- a/tests/test_quantity_class.py +++ b/tests/test_quantity_class.py @@ -5,7 +5,7 @@ # Valid Cases def test_valid(): - valid_floats = [1.0, 0.1, 4.0, 5.99999, 12455.321, 33, 1234, None] + valid_floats = [1.0, 0.1, 4.5, 5.99999, 12455.321, 33, 1234, None] for num in valid_floats: q = Quantity(value=num, unit="mg") assert q.value == num