diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 0000000000000000000000000000000000000000..d0ae97deb550cd6b8a26cbcebc34c55c8758bb60 --- /dev/null +++ b/coverage.xml @@ -0,0 +1,297 @@ +<?xml version="1.0" ?> +<coverage version="7.7.1" timestamp="1742813846643" lines-valid="207" lines-covered="156" line-rate="0.7536" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0"> + <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.7.1 --> + <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd --> + <sources> + <source>/home/user/Documents/Hindu/ReadtowriteExcel</source> + </sources> + <packages> + <package name="." line-rate="0.1639" branch-rate="0" complexity="0"> + <classes> + <class name="__init__.py" filename="__init__.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines/> + </class> + <class name="config.py" filename="config.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="18" hits="1"/> + <line number="21" hits="1"/> + <line number="22" hits="1"/> + <line number="25" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="31" hits="1"/> + </lines> + </class> + <class name="main.py" filename="main.py" complexity="0" line-rate="0" branch-rate="0"> + <methods/> + <lines> + <line number="2" hits="0"/> + <line number="4" hits="0"/> + <line number="5" hits="0"/> + <line number="6" hits="0"/> + <line number="7" hits="0"/> + </lines> + </class> + <class name="process_data.py" filename="process_data.py" complexity="0" line-rate="0" branch-rate="0"> + <methods/> + <lines> + <line number="3" hits="0"/> + <line number="4" hits="0"/> + <line number="5" hits="0"/> + <line number="6" hits="0"/> + <line number="7" hits="0"/> + <line number="11" hits="0"/> + <line number="13" hits="0"/> + <line number="14" hits="0"/> + <line number="17" hits="0"/> + <line number="24" hits="0"/> + <line number="25" hits="0"/> + <line number="27" hits="0"/> + <line number="28" hits="0"/> + <line number="29" hits="0"/> + <line number="30" hits="0"/> + <line number="33" hits="0"/> + <line number="36" hits="0"/> + <line number="37" hits="0"/> + <line number="38" hits="0"/> + <line number="40" hits="0"/> + <line number="43" hits="0"/> + <line number="45" hits="0"/> + <line number="46" hits="0"/> + <line number="48" hits="0"/> + <line number="49" hits="0"/> + <line number="50" hits="0"/> + <line number="53" hits="0"/> + <line number="54" hits="0"/> + <line number="55" hits="0"/> + <line number="56" hits="0"/> + <line number="57" hits="0"/> + <line number="59" hits="0"/> + <line number="60" hits="0"/> + <line number="61" hits="0"/> + <line number="64" hits="0"/> + <line number="65" hits="0"/> + <line number="67" hits="0"/> + <line number="68" hits="0"/> + <line number="70" hits="0"/> + <line number="71" hits="0"/> + <line number="72" hits="0"/> + <line number="74" hits="0"/> + <line number="77" hits="0"/> + <line number="83" hits="0"/> + <line number="86" hits="0"/> + <line number="87" hits="0"/> + </lines> + </class> + </classes> + </package> + <package name="tests" line-rate="1" branch-rate="0" complexity="0"> + <classes> + <class name="__init__.py" filename="tests/__init__.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines/> + </class> + <class name="test_api_client.py" filename="tests/test_api_client.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="5" hits="1"/> + <line number="7" hits="1"/> + <line number="8" hits="1"/> + <line number="9" hits="1"/> + <line number="11" hits="1"/> + <line number="12" hits="1"/> + <line number="15" hits="1"/> + <line number="17" hits="1"/> + <line number="18" hits="1"/> + <line number="29" hits="1"/> + <line number="31" hits="1"/> + <line number="32" hits="1"/> + <line number="34" hits="1"/> + <line number="35" hits="1"/> + <line number="37" hits="1"/> + <line number="39" hits="1"/> + <line number="40" hits="1"/> + <line number="41" hits="1"/> + <line number="43" hits="1"/> + <line number="44" hits="1"/> + </lines> + </class> + <class name="test_excel_operations.py" filename="tests/test_excel_operations.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="6" hits="1"/> + <line number="7" hits="1"/> + <line number="9" hits="1"/> + <line number="10" hits="1"/> + <line number="11" hits="1"/> + <line number="12" hits="1"/> + <line number="13" hits="1"/> + <line number="14" hits="1"/> + <line number="15" hits="1"/> + <line number="17" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="22" hits="1"/> + <line number="23" hits="1"/> + <line number="25" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="32" hits="1"/> + <line number="33" hits="1"/> + <line number="36" hits="1"/> + <line number="37" hits="1"/> + <line number="39" hits="1"/> + <line number="40" hits="1"/> + <line number="42" hits="1"/> + <line number="44" hits="1"/> + <line number="45" hits="1"/> + <line number="46" hits="1"/> + <line number="48" hits="1"/> + <line number="49" hits="1"/> + </lines> + </class> + <class name="test_payload_mapping.py" filename="tests/test_payload_mapping.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="6" hits="1"/> + <line number="7" hits="1"/> + <line number="15" hits="1"/> + <line number="16" hits="1"/> + <line number="18" hits="1"/> + <line number="21" hits="1"/> + <line number="22" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="30" hits="1"/> + <line number="33" hits="1"/> + <line number="34" hits="1"/> + <line number="36" hits="1"/> + <line number="38" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="47" hits="1"/> + <line number="48" hits="1"/> + <line number="51" hits="1"/> + <line number="53" hits="1"/> + </lines> + </class> + <class name="test_unpack_data.py" filename="tests/test_unpack_data.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="4" hits="1"/> + <line number="5" hits="1"/> + <line number="12" hits="1"/> + <line number="22" hits="1"/> + <line number="24" hits="1"/> + <line number="25" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="35" hits="1"/> + <line number="37" hits="1"/> + <line number="38" hits="1"/> + <line number="46" hits="1"/> + <line number="56" hits="1"/> + <line number="58" hits="1"/> + <line number="59" hits="1"/> + <line number="63" hits="1"/> + <line number="73" hits="1"/> + <line number="75" hits="1"/> + <line number="76" hits="1"/> + <line number="83" hits="1"/> + <line number="93" hits="1"/> + <line number="95" hits="1"/> + <line number="96" hits="1"/> + <line number="103" hits="1"/> + <line number="113" hits="1"/> + </lines> + </class> + </classes> + </package> + <package name="utils" line-rate="1" branch-rate="0" complexity="0"> + <classes> + <class name="__init__.py" filename="utils/__init__.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="2" hits="1"/> + </lines> + </class> + <class name="api_client.py" filename="utils/api_client.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="6" hits="1"/> + <line number="7" hits="1"/> + <line number="9" hits="1"/> + <line number="10" hits="1"/> + <line number="11" hits="1"/> + <line number="14" hits="1"/> + <line number="16" hits="1"/> + <line number="17" hits="1"/> + <line number="18" hits="1"/> + </lines> + </class> + <class name="excel_operations.py" filename="utils/excel_operations.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="2" hits="1"/> + <line number="4" hits="1"/> + <line number="6" hits="1"/> + <line number="8" hits="1"/> + <line number="10" hits="1"/> + <line number="12" hits="1"/> + <line number="14" hits="1"/> + <line number="15" hits="1"/> + <line number="16" hits="1"/> + <line number="17" hits="1"/> + </lines> + </class> + <class name="payload_mapping.py" filename="utils/payload_mapping.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="14" hits="1"/> + <line number="17" hits="1"/> + <line number="19" hits="1"/> + </lines> + </class> + <class name="unpack_data.py" filename="utils/unpack_data.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="7" hits="1"/> + <line number="8" hits="1"/> + <line number="9" hits="1"/> + <line number="12" hits="1"/> + <line number="13" hits="1"/> + <line number="16" hits="1"/> + </lines> + </class> + </classes> + </package> + </packages> +</coverage> diff --git a/sonar-scanner-cli-6.2.1.4610-linux-x64.zip b/sonar-scanner-cli-6.2.1.4610-linux-x64.zip new file mode 100644 index 0000000000000000000000000000000000000000..1299da9c6a23f7d85b976d53409a2d66eb5ace0a Binary files /dev/null and b/sonar-scanner-cli-6.2.1.4610-linux-x64.zip differ diff --git a/tests/__pycache__/test_excel_operations.cpython-310-pytest-8.3.5.pyc b/tests/__pycache__/test_excel_operations.cpython-310-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa126b281511f8cf325f913e270e4fa4cedce902 Binary files /dev/null and b/tests/__pycache__/test_excel_operations.cpython-310-pytest-8.3.5.pyc differ diff --git a/tests/__pycache__/test_unpack_data.cpython-310-pytest-8.3.5.pyc b/tests/__pycache__/test_unpack_data.cpython-310-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cae043b727cf556f9358e80b18850b9f68071735 Binary files /dev/null and b/tests/__pycache__/test_unpack_data.cpython-310-pytest-8.3.5.pyc differ diff --git a/tests/test_excel_operations.py b/tests/test_excel_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..76759dbb4493af14ef835c8c1f8d0d4c488af5a9 --- /dev/null +++ b/tests/test_excel_operations.py @@ -0,0 +1,49 @@ +import pytest +import openpyxl +from utils.excel_operations import load_excel, save_excel, create_output_workbook +import os + +@pytest.fixture +def sample_workbook(tmp_path): + """Create a sample workbook for testing.""" + file_path = tmp_path / "test.xlsx" + workbook = openpyxl.Workbook() + sheet = workbook.active + sheet.append(["Name", "Age", "City"]) + sheet.append(["John Doe", 30, "New York"]) + workbook.save(file_path) + return file_path + +def test_load_excel(sample_workbook): + """Test loading an existing Excel file.""" + workbook = load_excel(sample_workbook) + sheet = workbook.active + + assert sheet.cell(row=1, column=1).value == "Name" + assert sheet.cell(row=2, column=1).value == "John Doe" + +def test_save_excel(tmp_path): + """Test saving an Excel file.""" + workbook = openpyxl.Workbook() + sheet = workbook.active + sheet.append(["Product", "Price"]) + sheet.append(["Laptop", 1200]) + + file_path = tmp_path / "output.xlsx" + save_excel(workbook, file_path) + + # Verify the file is created and data is saved correctly + loaded_workbook = openpyxl.load_workbook(file_path) + loaded_sheet = loaded_workbook.active + + assert loaded_sheet.cell(row=1, column=1).value == "Product" + assert loaded_sheet.cell(row=2, column=2).value == 1200 + +def test_create_output_workbook(): + """Test creating a new workbook with headers.""" + headers = ["ID", "Name", "Department"] + workbook = create_output_workbook(headers) + sheet = workbook.active + + assert sheet.cell(row=1, column=1).value == "ID" + assert sheet.cell(row=1, column=3).value == "Department" diff --git a/tests/test_unpack_data.py b/tests/test_unpack_data.py new file mode 100644 index 0000000000000000000000000000000000000000..1bc253c06a2dbc3601a37ba71c4d0b3066144003 --- /dev/null +++ b/tests/test_unpack_data.py @@ -0,0 +1,113 @@ +import pytest +from utils.unpack_data import unpack_input_data # Import the function + +def test_valid_input(): + input_data = { + "Cefr Level": "B2", + "Scenario": "Business Email", + "Scenario Question": "How to write a formal email?", + "Subject": "Job Inquiry", + "Email Content": "Dear HR, I am interested in the position..." + } + expected_output = { + "cefr_level": "B2", + "scenario": "Business Email", + "scenario_question": "How to write a formal email?", + "subject": "Job Inquiry", + "email_content": "Dear HR, I am interested in the position...", + "user_id": 7600001, + "question_id": 100456, + "answer_id": 120000789 + } + assert unpack_input_data(input_data) == expected_output + +def test_empty_input(): + assert unpack_input_data({}) is None + +def test_all_empty_values(): + input_data = { + "Cefr Level": "", + "Scenario": "", + "Scenario Question": "", + "Subject": "", + "Email Content": "" + } + assert unpack_input_data(input_data) is None + +def test_extra_fields(): + input_data = { + "Cefr Level": "A1", + "Scenario": "Casual", + "Scenario Question": "What is a friendly email?", + "Subject": "Hi there!", + "Email Content": "Hey! Hope you're doing well.", + "Extra Field": "Should be ignored" + } + expected_output = { + "cefr_level": "A1", + "scenario": "Casual", + "scenario_question": "What is a friendly email?", + "subject": "Hi there!", + "email_content": "Hey! Hope you're doing well.", + "user_id": 7600001, + "question_id": 100456, + "answer_id": 120000789 + } + assert unpack_input_data(input_data) == expected_output + +def test_partial_fields(): + input_data = { + "Cefr Level": "C1", + "Scenario": "Technical Writing" + } + expected_output = { + "cefr_level": "C1", + "scenario": "Technical Writing", + "scenario_question": "", + "subject": "", + "email_content": "", + "user_id": 7600001, + "question_id": 100456, + "answer_id": 120000789 + } + assert unpack_input_data(input_data) == expected_output + +def test_whitespace_handling(): + input_data = { + "Cefr Level": " A2 ", + "Scenario": " Formal ", + "Scenario Question": " How to apply for a job? ", + "Subject": " Job Application ", + "Email Content": " Please find my resume attached. " + } + expected_output = { + "cefr_level": "A2", + "scenario": "Formal", + "scenario_question": "How to apply for a job?", + "subject": "Job Application", + "email_content": "Please find my resume attached.", + "user_id": 7600001, + "question_id": 100456, + "answer_id": 120000789 + } + assert unpack_input_data(input_data) == expected_output + +def test_none_values(): + input_data = { + "Cefr Level": None, + "Scenario": "Some scenario", + "Scenario Question": None, + "Subject": None, + "Email Content": None + } + expected_output = { + "cefr_level": "", + "scenario": "Some scenario", + "scenario_question": "", + "subject": "", + "email_content": "", + "user_id": 7600001, + "question_id": 100456, + "answer_id": 120000789 + } + assert unpack_input_data(input_data) == expected_output diff --git a/utils/__pycache__/payload_mapping.cpython-310.pyc b/utils/__pycache__/payload_mapping.cpython-310.pyc index 1ea7b6d76b6eb27bb775def279c43c93e2548db0..1a93378760da7f857f555af134348d38ea6f5c0b 100644 Binary files a/utils/__pycache__/payload_mapping.cpython-310.pyc and b/utils/__pycache__/payload_mapping.cpython-310.pyc differ diff --git a/utils/__pycache__/unpack_data.cpython-310.pyc b/utils/__pycache__/unpack_data.cpython-310.pyc index 536a455ec684288462331114e7d4f4c481b62146..c19145a50a7ca4b7f93a396857aae9852a204528 100644 Binary files a/utils/__pycache__/unpack_data.cpython-310.pyc and b/utils/__pycache__/unpack_data.cpython-310.pyc differ diff --git a/utils/payload_mapping.py b/utils/payload_mapping.py index b8aa874ec97ae1a7e4b4bba61ecb6bd649829d25..de4320aba1df114c720b85cb78932f943f74ccf1 100644 --- a/utils/payload_mapping.py +++ b/utils/payload_mapping.py @@ -1,30 +1,3 @@ -# # utils/payload_mapping.py -# -# def get_payload_mapping(cefr_level, scenario, scenario_question, subject, email_content): -# """ -# Maps input data to the payload structure required by the API with fixed user_id, question_id, and answer_id. -# -# Args: -# cefr_level (str): The CEFR level of the input data. -# scenario (str): The scenario description. -# scenario_question (str): The scenario question. -# subject (str): The subject or topic. -# email_content (str): The email content. -# -# Returns: -# dict: The payload ready to be sent to the API. -# """ -# return { -# "cefr_level": cefr_level, -# "user_id": 7600001, # Static user ID -# "question_id": 100456, # Static question ID -# "answer_id": 120000789, # Static answer ID -# "scenario": scenario, -# "scenario_question": scenario_question, -# "subject": subject, -# "email_content": email_content -# } - from config import PAYLOAD_FIELDS_STATIC def get_payload_mapping(input_data): diff --git a/utils/unpack_data.py b/utils/unpack_data.py index 48d7df68f2e78bbbf0d8771495bed0525225d628..5cbaa21e89fa75c79a46e34d66e806d3ce4f4212 100644 --- a/utils/unpack_data.py +++ b/utils/unpack_data.py @@ -1,21 +1,12 @@ from config import INPUT_FIELDS_DYNAMIC from utils.payload_mapping import get_payload_mapping - def unpack_input_data(input_data): - """ - Unpacks input data dynamically using JSON-defined mappings. - - Args: - input_data (dict): The dictionary containing raw input data (e.g., from Excel). - - Returns: - dict or None: The prepared payload for the API request or None if the row is empty. - """ unpacked_data = {} # Loop through INPUT_FIELDS mapping from config.json to dynamically unpack data for excel_field, payload_key in INPUT_FIELDS_DYNAMIC.items(): - unpacked_data[payload_key] = input_data.get(excel_field, "").strip() # Strip whitespace + value = input_data.get(excel_field, "") # Get value, default to empty string if missing + unpacked_data[payload_key] = value.strip() if isinstance(value, str) else "" # Convert None to "" # Check if all unpacked values are empty if all(value == "" for value in unpacked_data.values()): @@ -23,3 +14,8 @@ def unpack_input_data(input_data): # Pass valid data to get_payload_mapping return get_payload_mapping(unpacked_data) + + + + +