From 3a8383f2e177808a6b0217febe5cd93a77636238 Mon Sep 17 00:00:00 2001 From: Alireza Aghamohammadi Date: Wed, 20 Dec 2023 12:43:57 +0330 Subject: [PATCH] Implement XML Report Generation and Expand Testing - Imported xml.etree.ElementTree module in report.py for XML support. - Introduced `generate_optimal_orientation_xml_report` method in ReportGenerator. - The new method generates an XML formatted report of optimal solar panel orientations. - Extended the `ReportGenerator` class to include data serialization into XML format. - Augmented unit tests to validate XML report output in test_pysolorie.py. - Ensured consistency and efficacy of the XML report content through comprehensive testing. --- src/pysolorie/report.py | 48 +++++++++++++++++++++++++++++++++++++++++ tests/test_pysolorie.py | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/pysolorie/report.py b/src/pysolorie/report.py index 0b0f392..03f0fca 100644 --- a/src/pysolorie/report.py +++ b/src/pysolorie/report.py @@ -15,6 +15,7 @@ import csv import json import logging +import xml.etree.ElementTree as ET from pathlib import Path from typing import Dict, List, Union @@ -154,3 +155,50 @@ def generate_optimal_orientation_json_report( # Write the data list to the JSON file with open(path, "w") as file: json.dump(data, file, indent=4) + + @logger_decorator + def generate_optimal_orientation_xml_report( + self, + path: Path, + irradiation_calculator: IrradiationCalculator, + from_day: int, + to_day: int, + ) -> None: + r""" + This method generates a report of optimal solar panel orientation in XML format. + It uses the ``_calculate_optimal_orientation_and_irradiation`` + method to get the data. + + :param path: A Path object that points to the XML file + where the report will be written. + :type path: Path + :param irradiation_calculator: An instance of the IrradiationCalculator class. + :type irradiation_calculator: pysolorie.IrradiationCalculator + :param from_day: The starting day of the report. + :type from_day: int + :param to_day: The ending day of the report. + :type to_day: int + """ + data = self._calculate_optimal_orientation_and_irradiation( + irradiation_calculator, from_day, to_day + ) + + # Create the root element + root = ET.Element("Report") + + for row in data: + # Create a 'Day' element for each day + day_element = ET.SubElement(root, "Day") + day_element.set("id", str(row["Day"])) + + # Create 'Beta' and 'TotalDirectIrradiation' elements for each day + beta_element = ET.SubElement(day_element, "Beta") + beta_element.text = str(row["Beta (degrees)"]) + tdi_element = ET.SubElement(day_element, "TotalDirectIrradiation") + tdi_element.text = str( + row["Total Direct Irradiation (Megajoules per square meter)"] + ) + + # Write the XML data to the file + tree = ET.ElementTree(root) + tree.write(path) diff --git a/tests/test_pysolorie.py b/tests/test_pysolorie.py index a977e64..5161812 100644 --- a/tests/test_pysolorie.py +++ b/tests/test_pysolorie.py @@ -14,6 +14,7 @@ import csv import json import math +import xml.etree.ElementTree as ET from pathlib import Path from typing import Any, Dict, List, Tuple from unittest.mock import MagicMock @@ -431,6 +432,50 @@ def test_generate_optimal_orientation_json_report(tmpdir) -> None: ) +def test_generate_optimal_orientation_xml_report(tmpdir) -> None: + # Create a temporary directory for the test + temp_dir: Path = Path(tmpdir) + + # Initialize the ReportGenerator + report_generator: ReportGenerator = ReportGenerator() + + # Initialize the IrradiationCalculator for Tehran + irradiation_calculator: IrradiationCalculator = IrradiationCalculator( + "MIDLATITUDE SUMMER", 1200, 35.6892 + ) + + # Define the path for the XML file + xml_path: Path = temp_dir / "report.xml" + from_day: int = 60 + to_day: int = 70 + # Call the method to generate the report + report_generator.generate_optimal_orientation_xml_report( + xml_path, irradiation_calculator, from_day, to_day + ) + + # Check the XML file + tree = ET.parse(xml_path) + root = tree.getroot() + + for i, day_element in enumerate(root.findall("Day"), start=from_day): + day = int(day_element.get("id")) + beta = float(day_element.find("Beta").text) + total_direct_irradiation = float( + day_element.find("TotalDirectIrradiation").text + ) + + assert day == i + expected_beta = irradiation_calculator.find_optimal_orientation(i) + expected_total_direct_irradiation = ( + irradiation_calculator.calculate_direct_irradiation(beta, i) + ) + assert pytest.approx(beta, abs=1e-3) == expected_beta + assert ( + pytest.approx(total_direct_irradiation, abs=1e-3) + == expected_total_direct_irradiation + ) + + def test_plot_optimal_orientation(tmpdir) -> None: # Create a temporary directory for the test temp_dir: Path = Path(tmpdir)