Repository: cunybpl/isdparser

Issue:
ISDRecordFactory's constructor bug
There is a bug in constructor of ISDRecordFactory. 
```[python]
class ISDRecordFactory(object):
        __slots__ = ('_control', '_mandatory')
        def __init__(self, 
                         control: Tuple[str, List[Measure]]=('control', control_measures()), 
                         mandatory: Tuple[str, List[Measure]]=('mandatory', mandatory_measures())):
                """Creates an ISDRecord from a single line from an isd file. 
                Object configuration uses Section and Measure object APIs internally. 
        
                Args:
                    control (Tuple[str, List[Measure]], optional): A name and list of control measures. 
                        Defaults to ('control', control_measures()).
                    mandatory (Tuple[str, List[Measure]], optional): A name and list of section measures. 
                        Defaults to ('mandatory', mandatory_measures()).
                """
                self._control = control
                self._mandatory = mandatory

        def create(self, line: str) -> ISDRecord:
                """Create an ISDRecord with a line of isd data extracted from a file.
        
                Args:
                    line (str): A single line of raw isd data.
        
                Returns:
                    ISDRecord: The resulting ISDRecord object.
                """
                control, mandatory = Section(*self._control), Section(*self._mandatory)
        
```
And we call it: `[ISDRecordFactory().create(line) for line in lines]`. Whenever `ISDRecordFactroy.create` get called, it is referring to the previous `self._control` and `self._mandatory` objects. Hence, by modifying this way:
```[python]
class ISDRecordFactory(object):
        __slots__ = ('_control', '_mandatory')
        def __init__(self, 
                         control: Tuple[str, List[Measure]]=None, 
                         mandatory: Tuple[str, List[Measure]]=None:
                """Creates an ISDRecord from a single line from an isd file. 
                Object configuration uses Section and Measure object APIs internally. 
        
                Args:
                    control (Tuple[str, List[Measure]], optional): A name and list of control measures. 
                        Defaults to ('control', control_measures()).
                    mandatory (Tuple[str, List[Measure]], optional): A name and list of section measures. 
                        Defaults to ('mandatory', mandatory_measures()).
                """
                self._control = control if control else ('control', control_measures())
                self._mandatory = mandatory if mandatory else ('mandatory', mandatory_measures()))
```
the bug can be fixed.

Discussion:

Patch:
diff --git a/isdparser/_version.py b/isdparser/_version.py
index aa70592..b72bd09 100644
--- a/isdparser/_version.py
+++ b/isdparser/_version.py
@@ -1,1 +1,1 @@
-VERSION='0.2.0'
\ No newline at end of file
+VERSION='0.2.1'
\ No newline at end of file
diff --git a/isdparser/record.py b/isdparser/record.py
index 3e70bfe..78c1885 100644
--- a/isdparser/record.py
+++ b/isdparser/record.py
@@ -49,19 +49,21 @@ class ISDRecordFactory(object):
     __slots__ = ('_control', '_mandatory')
     
     def __init__(self, 
-                 control: Tuple[str, List[Measure]]=('control', control_measures()), 
-                 mandatory: Tuple[str, List[Measure]]=('mandatory', mandatory_measures())):
+                 control: Tuple[str, List[Measure]]=None, 
+                 mandatory: Tuple[str, List[Measure]]=None):
         """Creates an ISDRecord from a single line from an isd file. 
         Object configuration uses Section and Measure object APIs internally. 
+        If control and mandatory were not given, ('control', control_measures()) and ('mandatory', mandatory_measures())) 
+        will be used instead.
 
         Args:
             control (Tuple[str, List[Measure]], optional): A name and list of control measures. 
-                Defaults to ('control', control_measures()).
+                Defaults to None.
             mandatory (Tuple[str, List[Measure]], optional): A name and list of section measures. 
-                Defaults to ('mandatory', mandatory_measures()).
+                Defaults to None.
         """
-        self._control = control
-        self._mandatory = mandatory
+        self._control = control if control else ('control', control_measures())
+        self._mandatory = mandatory if mandatory else ('mandatory', mandatory_measures())
         
 
     @staticmethod 


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 5fb53a3..b61f1fc 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,8 +2,12 @@ import pytest
 
 
 @pytest.fixture
-def isd_record_string(): 
-    return """0130010230999992020010100004+69067+018533FM-12+007999999V0200501N001019999999N999999999-00291-00381099661
-    ADDAA199999919MA1999999098701MD1810211+9999OD139900601999REMSYN06401023 16/// /0501 11029 21038 39870 49966 58021 6//// 333 91106="""
+def isd_record_strings_list(): 
+    return ["""0130010230999992020010100004+69067+018533FM-12+007999999V0200501N001019999999N999999999-00291-00381099661
+    ADDAA199999919MA1999999098701MD1810211+9999OD139900601999REMSYN06401023 16/// /0501 11029 21038 39870 49966 58021 6//// 333 91106=""",
+    """0130010230999992020020100004+69067+018533FM-12+007999999V0200501N001019999999N999999999-00291-00381099661
+    ADDAA199999919MA1999999098701MD1810211+9999OD139900601999REMSYN06401023 16/// /0501 11029 21038 39870 49966 58021 6//// 333 91106=""",
+    """0130010230999992020030100004+69067+018533FM-12+007999999V0200501N001019999999N999999999-00291-00381099661
+    ADDAA199999919MA1999999098701MD1810211+9999OD139900601999REMSYN06401023 16/// /0501 11029 21038 39870 49966 58021 6//// 333 91106="""]
 
 
diff --git a/tests/test_record.py b/tests/test_record.py
index 9ac717e..84b3f0d 100644
--- a/tests/test_record.py
+++ b/tests/test_record.py
@@ -5,6 +5,7 @@ tc = unittest.TestCase()
 
 from isdparser.measures import Measure, Section
 from isdparser.record import ISDRecord, ISDRecordFactory
+import copy
 
 def test_isdrecord(mocker): 
 
@@ -48,11 +49,12 @@ def test_isdrecordfactory_static_methods():
 
 
 
-def test_isdrecordfactory_on_record_string(isd_record_string):
+def test_isdrecordfactory_on_record_string(isd_record_strings_list):
     
-    result = ISDRecordFactory().create(isd_record_string)
 
-    expected = {
+    result = [ISDRecordFactory().create(line) for line in isd_record_strings_list]
+
+    exp0 = {
         'datestamp': datetime.datetime(2020, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), 
         'identifier': '010230-99999', 
         'sections': [
@@ -97,4 +99,17 @@ def test_isdrecordfactory_on_record_string(isd_record_string):
         ]
     }
 
-    tc.assertDictEqual(expected, result.schema())
\ No newline at end of file
+    exp1 = copy.deepcopy(exp0)
+    exp1['datestamp'] = datetime.datetime(2020, 2, 1, 0, 0, tzinfo=datetime.timezone.utc)
+    exp1['sections'][0]['measures'][2]['value'] = '20200201'
+
+    exp2 = copy.deepcopy(exp0)
+    exp2['datestamp'] = datetime.datetime(2020, 3, 1, 0, 0, tzinfo=datetime.timezone.utc)
+    exp2['sections'][0]['measures'][2]['value'] = '20200301'
+
+    expected = [exp0, exp1, exp2]
+
+    for res in result:
+        datestamp = res.schema()['datestamp']
+        exp = list(filter(lambda x: x['datestamp'] == datestamp, expected))[0]
+        tc.assertDictEqual(exp, res.schema())
\ No newline at end of file