Repository: RockefellerArchiveCenter/DACSspace

Issue:
Create reporting class
As part of #21, create a class which handles reporting logic. Currently this is written to a CSV but we could also print it to stdout, or possibly other formats.

Discussion:

Patch:
diff --git a/dacsspace/reporter.py b/dacsspace/reporter.py
index 731b7a8..514a247 100644
--- a/dacsspace/reporter.py
+++ b/dacsspace/reporter.py
@@ -1,10 +1,13 @@
+import csv
+
 
 class CSVReporter:
     """Creates CSV reports."""
 
-    def __init__(self):
-        # TODO: set filepath for CSV
-        pass
+    def __init__(self, filename, filemode="w"):
+        """Sets the filename and filemode."""
+        self.filename = filename
+        self.filemode = filemode
 
     def write_report(self, results, invalid_only=True):
         """Writes results to a CSV file.
@@ -13,4 +16,15 @@ class CSVReporter:
             results (list): A list of dictionaries containing information about validation results.
             invalid_only (boolean): Only report on invalid results.
         """
-        pass
+
+        if self.filemode.startswith("r"):
+            raise ValueError("Filemode must allow write options.")
+        with open(self.filename, self.filemode) as f:
+            fieldnames = [
+                "valid",
+                "explanation"]
+            writer = csv.DictWriter(
+                f, fieldnames=fieldnames)
+            writer.writeheader()
+            filtered_results = [row for row in results if not row["valid"]] if invalid_only else results
+            writer.writerows(filtered_results)
diff --git a/tox.ini b/tox.ini
index c3ec76b..32e8736 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py39
+envlist = py310
 skipsdist = True
 
 [testenv]


Test patch:
diff --git a/tests/test_reporter.py b/tests/test_reporter.py
index f2d4612..92b27c9 100644
--- a/tests/test_reporter.py
+++ b/tests/test_reporter.py
@@ -1,7 +1,44 @@
-# from unittest.mock import patch
-#
-# from dacsspace.reporter import CSVReporter
+import os
+from unittest import TestCase
+from unittest.mock import patch
 
+from dacsspace.reporter import CSVReporter
 
-def test_reporter():
-    pass
+
+class CSVReporterTest(TestCase):
+
+    def setUp(self):
+        """Sets filename and data attributes for test file.
+
+        Checks if test file exists, then deletes it.
+        """
+        self.filename = "DACSSpace_results"
+        self.results = [{"valid": True, "explanation": None}, {"valid": False, "explanation": "No title"}]
+        if os.path.isfile(self.filename):
+            os.remove(self.filename)
+
+    def test_CSV(self):
+        """Asserts that the results are correctly written to the file.
+
+        Raises an error if the file has an incorrect filemode and asserts that the filemode must allow write options.
+        """
+        CSVReporter(self.filename).write_report(self.results)
+        self.assertTrue(self.filename)
+        with self.assertRaises(ValueError) as err:
+            CSVReporter(self.filename, "r").write_report(self.results)
+        self.assertEqual(str(err.exception), "Filemode must allow write options.")
+
+    @patch("csv.DictWriter.writerows")
+    def test_invalid(self, mock_writerows):
+        """Mocks writing only invalid results and valid results to file."""
+        CSVReporter(self.filename).write_report(self.results)
+        mock_writerows.assert_called_with([{"valid": False, "explanation": "No title"}])
+        CSVReporter(self.filename).write_report(self.results, invalid_only=False)
+        mock_writerows.assert_called_with(self.results)
+
+    def tearDown(self):
+        """Tears down test file.
+
+        Checks if test file exists, then deletes it."""
+        if os.path.isfile(self.filename):
+            os.remove(self.filename)