From cbd7237ae01e195ffb5bdf9e86c7c251e38375f0 Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Tue, 13 Jun 2023 15:34:38 +0100 Subject: [PATCH 01/11] feat(pain001): v0.0.20 --- README.md | 108 ++++---- TEMPLATE.md | 2 +- docs/404.html | 6 + pain001/__init__.py | 2 +- pain001/__main__.py | 250 +++++++++--------- pain001/context/context.py | 28 +- pain001/core/__init__.py | 14 + pain001/{ => core}/core.py | 78 +++--- pain001/xml/create_root_element.py | 5 +- pain001/xml/generate_updated_xml_file_path.py | 5 +- pain001/xml/generate_xml.py | 26 +- pain001/xml/validate_via_xsd.py | 37 ++- pain001/xml/xml_generator.py | 9 +- pyproject.toml | 2 +- setup.cfg | 2 +- setup.py | 2 +- templates/pain.001.001.03/template.db | Bin 0 -> 8192 bytes templates/pain.001.001.03/template.xml | 10 + tests/test_context.py | 73 +++-- tests/test_core.py | 28 +- tests/test_main.py | 183 +++++++++---- 21 files changed, 503 insertions(+), 367 deletions(-) create mode 100644 docs/404.html create mode 100644 pain001/core/__init__.py rename pain001/{ => core}/core.py (71%) create mode 100644 templates/pain.001.001.03/template.db diff --git a/README.md b/README.md index 9843c09..fa946f8 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,56 @@ - - -Pain001 Logo - - - -# Pain001 - A Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data +# Pain001: Automate ISO 20022-Compliant Payment File Creation ![Pain001 banner][banner] [![PyPI][pypi-badge]][3] [![License][license-badge]][1] [![Codecov][codecov-badge]][6] -**Pain001** is a Python Library for Automating ISO 20022-Compliant Payment -Files Using CSV Data. - -**Pain001** offers a streamlined solution for reducing complexity and costs -associated with payment processing. By providing a simple and efficient method -to create ISO 20022-compliant payment files, it eliminates the manual effort of -file creation and validation. This not only saves valuable time and resources -but also minimizes the risk of errors, ensuring accurate and seamless payment -processing. +**Pain001** is a powerful Python library that enables you to create ISO 20022- +compliant payment files directly from CSV or SQLite Data Files. + +## Overview + +Today, the payment industry is experiencing a rapid evolution and +transformation as it moves towards adopting **[ISO 20022][1]** as the new norm. +ISO 20022 is a global standard for sharing financial information across +organisations. It provides a harmonised protocol used by banks, corporations, +and financial institutions to automate and standardise payment transactions. + +Here are some of the benefits of using ISO 20022: + +- **Improved data quality**: ISO 20022 messages are more structured and + detailed than traditional payment messages, reducing errors and improving + efficiency. +- **Increased transparency**: ISO 20022 messages provide more information + about the payment transaction, which can help to improve the visibility and + traceability of payments. +- **Enhanced compliance**: ISO 20022 messages can help organisations follow + AML/CFT and PSD2 regulations. + +Overall, ISO 20022 is a significant improvement over traditional payment +messaging standards. It provides a more efficient, transparent, and compliant +way to process payments. + +## Pain001 in action + +The Python library **Pain001** focuses specifically on payment initiation and +advice messages, commonly known as **pain**. Payments usually start with a +**pain.001 payment initiation message**. The payer sends it to the payee (or +the payee's bank) via a secure network. This network could be **SWIFT** or +**SEPA (Single Euro Payments Area)** network, or other payment networks such +as **CHAPS**, **BACS**, **Faster Payments**, etc. The message contains the +payer's and payee's bank account details, payment amount, and other information +required to process the payment. + +**Pain001** can reduce payment processing complexity and costs by generating +ISO 20022-compliant payment files. These files automatically remove the need to +create and validate them manually, making the payment process more efficient +and cost-effective. It will save you time and resources and minimises the risk +of errors, making sure accurate and seamless payment processing. If you are seeking to simplify and automate your payment processing, consider leveraging the capabilities of **Pain001**. -## Features ✨ - -- **Easy to use:** The library is easy to use and requires minimal coding - knowledge, making it suitable for both developers and non-developers. -- **Open-source**: The library is open-source and free to use, making it - accessible to everyone. -- **Secure**: The library is secure and does not store any sensitive data, - ensuring that all information remains confidential. -- **Customizable**: The library allows developers to customize the output, - making it adaptable to specific business requirements and preferences. -- **Scalable solution**: The **Pain001** library can handle varying volumes of - payment files, making it suitable for businesses of different sizes and - transaction volumes. -- **Time-saving**: The automated file creation process reduces the time spent - on manual data entry and file generation, increasing overall productivity. -- **Seamless integration**: As a Python package, the **Pain001** library is - compatible with various Python-based applications and easily integrates into - any existing projects or workflows. -- **Cross-border compatibility**: The library supports both Single Euro - Payments Area (SEPA) and non-SEPA credit transfers, making it versatile for - use in different countries and regions. -- **Improve accuracy** by providing precise data, the library reduces errors in - payment file creation and processing. -- **Enhance efficiency** by automating the creation of Payment Initiation - message files -- **Accelerate payment file creation** by automating the process and reducing - the time required to create payment files. -- **Guarantee the highest quality and compliance** by validating all payment - files to meet the ISO 20022 standards. -- **Provide flexibility and choice to migrate to any supported ISO 20022 - messaging standard definitions** by simplifying the message creation process - and providing a standardized format for payment files. - ## Installation It takes just a few seconds to get up and running with **Pain001**. Open your @@ -86,7 +74,8 @@ python3 -m pain001 \ \ \ \ - + \ + ``` ## Arguments @@ -101,6 +90,8 @@ When running **Pain001**, you will need to specify four arguments: - `xsd_file_path`: This is the path to the XSD template file you are using. - `csv_file_path`: This is the path to the CSV data file you want to convert to XML. +- `output_file_path`: This is the path to the output XML file you want to save + the generated XML file to. ## Examples @@ -114,7 +105,8 @@ python3 -m pain001 \ pain.001.001.03 \ /path/to/your/pain.001.001.03.xml \ /path/to/your/pain.001.001.03.xsd \ - /path/to/your/pain.001.001.03.csv + /path/to/your/pain.001.001.03.csv \ + /path/to/your/output.xml ``` **Note:** The XML file that **Pain001** generates will be automatically @@ -299,7 +291,7 @@ of [Pain001][5] for their help and support. [pain.001.001.10]: docs/payments-initiation/messages/pain.001.001.10/README.md [pain.001.001.11]: docs/payments-initiation/messages/pain.001.001.11/README.md -[banner]: https://kura.pro/pain001/images/titles/title-pain001.svg 'Pain001' +[banner]: https://kura.pro/pain001/images/banners/banner-pain001.svg 'Pain001' [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/pain001?style=for-the-badge&token=AaUxKfRiou 'Codecov badge' [license-badge]: https://img.shields.io/pypi/l/pain001?style=for-the-badge 'License badge' [pypi-badge]: https://img.shields.io/pypi/pyversions/pain001.svg?style=for-the-badge 'PyPI badge' diff --git a/TEMPLATE.md b/TEMPLATE.md index 6f10c54..801dc3b 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -10,7 +10,7 @@ -# Pain001 (v0.0.19) +# Pain001 (v0.0.20) ![Pain001 banner][banner] diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..a0d088c --- /dev/null +++ b/docs/404.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/pain001/__init__.py b/pain001/__init__.py index 858b938..51a2f95 100644 --- a/pain001/__init__.py +++ b/pain001/__init__.py @@ -15,4 +15,4 @@ """The Python pain001 module.""" __all__ = ["pain001"] -__version__ = "0.0.19" +__version__ = "0.0.20" diff --git a/pain001/__main__.py b/pain001/__main__.py index fa3ca4b..d93d239 100644 --- a/pain001/__main__.py +++ b/pain001/__main__.py @@ -17,142 +17,142 @@ """ Enables use of Python Pain001 as a "main" function (i.e. "python3 -m pain001 - "). + +"). This allows using Pain001 with third-party libraries without modifying their code. """ -from pain001.core import process_files -from pain001.context import context -from pain001.constants.constants import valid_xml_types import os import sys -import argparse +import click +from pain001.constants.constants import valid_xml_types +from pain001.context.context import Context +from pain001.core.core import process_files from pain001.xml.validate_via_xsd import validate_via_xsd +from rich.console import Console +from rich.table import Table +from rich import box -cli_string = """ -**Pain001** is a Python Library for Automating ISO 20022-Compliant Payment -Files Using CSV Data. - -**Pain001** offers a streamlined solution for reducing complexity and costs -associated with payment processing. By providing a simple and efficient method -to create ISO 20022-compliant payment files, it eliminates the manual effort of -file creation and validation. This not only saves valuable time and resources -but also minimizes the risk of errors, ensuring accurate and seamless payment -processing. - -If you are seeking to simplify and automate your payment processing, consider -leveraging the capabilities of **Pain001**. - -## Installation - -To install **Pain001**, run this command in your terminal: - -```sh -pip install pain001 -``` - -## Usage - -To use **Pain001**, run this command in your terminal: - -```sh -python3 -m pain001 \ - \ - \ - \ - -``` - -## Arguments: +console = Console() -- `xml_message_type`: The type of XML message. The current valid values are: - - pain.001.001.03 and - - pain.001.001.09 -- `xml_file_path`: The path to the XML template file. -- `xsd_file_path`: The path to the XSD template file. -- `csv_file_path`: The path to the CSV data file. +description = """ +A powerful Python library that enables you to create +ISO 20022-compliant payment files directly from CSV or SQLite Data files.\n +https://pain001.com +""" +title = "Pain001" + +table = Table( + box=box.ROUNDED, + safe_box=True, + show_header=False, + title=title, +) + +table.add_column(justify="center", no_wrap=False, vertical="middle") +table.add_row(description) +table.width = 80 +console.print(table) + + +@click.command( + help=( + "To use Pain001, you must specify the following options:\n\n" + ), + context_settings=dict(help_option_names=["-h", "--help"]), +) +@click.option( + "-t", + "--xml_message_type", + default=None, + help="Type of XML message (required)", +) +@click.option( + "-m", + "--xml_template_file_path", + default=None, + type=click.Path(), + help="Path to XML template file (required)", +) +@click.option( + "-s", + "--xsd_template_file_path", + default=None, + type=click.Path(), + help="Path to XSD template file (required)", +) +@click.option( + "-d", + "--data_file_path", + default=None, + type=click.Path(), + help="Path to data file (CSV or SQLite) (required)", +) +def main( + xml_message_type, + xml_template_file_path, + xsd_template_file_path, + data_file_path, +): + """Initialize the context and log a message.""" + logger = Context.get_instance().get_logger() -## Example: + # print("Inside main function") -To generate a pain.001.001.03 XML file from the CSV data file you can run the -following command in your terminal: + def check_variable(variable, name): + if variable is None: + print(f"Error: {name} is required.") + sys.exit(1) -```sh -python3 -m pain001 \ - pain.001.001.03 \ - /path/to/your/pain.001.001.03.xml \ - /path/to/your/pain.001.001.03.xsd \ - /path/to/your/pain.001.001.03.csv -``` + # Check that xml_message_type is provided + check_variable(xml_message_type, "xml_message_type") -Note: The generated XML file will be validated against the XSD template -file before being saved. If the validation fails, the program will exit -with an error message. + # Check that xsd_template_file_path is provided + check_variable(xsd_template_file_path, "xsd_template_file_path") -For more information, please visit the project's GitHub page at: -. -""" + # Check that data_file_path is provided + check_variable(data_file_path, "data_file_path") + # Check that xml_template_file_path is not invalid + if not os.path.isfile(xml_template_file_path): + print( + f"The XML template file '{xml_template_file_path}' does not exist." + ) + sys.exit(1) -def main( - xml_message_type=None, - xml_file_path=None, - xsd_file_path=None, - data_file_path=None, - output_file_path=None, -): - """ - Entrypoint for pain001 when invoked as a module with - python3 -m pain001 - . - """ + # Check that xsd_template_file_path is not invalid + if not os.path.isfile(xsd_template_file_path): + print( + f"The XSD template file '{xsd_template_file_path}' does not exist." + ) + sys.exit(1) - """Initialize the context and log a message.""" - logger = context.Context.get_instance().get_logger() + # Check that data_file_path is not invalid + if not os.path.isfile(data_file_path): + print(f"The data file '{data_file_path}' does not exist.") + sys.exit(1) + # Check that other necessary arguments are provided if ( - xml_file_path is None - or xsd_file_path is None + xml_template_file_path is None + or xsd_template_file_path is None or data_file_path is None - or output_file_path is None ): - parser = argparse.ArgumentParser( - description="Generate Pain.001 file from data" - ) - parser.add_argument( - "xml_message_type", help="Type of XML message" - ) - parser.add_argument( - "xml_file_path", help="Path to XML template file" - ) - parser.add_argument( - "xsd_file_path", help="Path to XSD template file" - ) - parser.add_argument( - "data_file_path", help="Path to data file (CSV or SQLite)" - ) - parser.add_argument( - "output_file_path", help="Path to output XML file" - ) - args = parser.parse_args() - - logger.info("Parsing command line arguments.") - xml_message_type = args.xml_message_type - xml_file_path = args.xml_file_path - xsd_file_path = args.xsd_file_path - data_file_path = args.data_file_path - output_file_path = args.output_file_path - - """Check that the files or values passed as arguments exist.""" - if not xml_message_type: - logger.info("The XML message type is not specified.") - print("The XML message type is not specified.") + print(click.get_current_context().get_help()) sys.exit(1) + """ + Entrypoint for pain001 when invoked as a module with + python3 -m pain001 + . + """ + logger = Context.get_instance().get_logger() + + logger.info("Parsing command line arguments.") # Check that the XML message type is valid if xml_message_type not in valid_xml_types: @@ -160,18 +160,22 @@ def main( print(f"Invalid XML message type: {xml_message_type}.") sys.exit(1) - if not os.path.isfile(xml_file_path): + if not os.path.isfile(xml_template_file_path): logger.info( - "The XML template file '{xml_file_path}' does not exist." + f"The XML template file '{xml_template_file_path}' does not exist." + ) + print( + f"The XML template file '{xml_template_file_path}' does not exist." ) - print("The XML template file '{xml_file_path}' does not exist.") sys.exit(1) - if not os.path.isfile(xsd_file_path): + if not os.path.isfile(xsd_template_file_path): logger.info( - "The XSD template file '{xsd_file_path}' does not exist." + f"The XSD template file '{xsd_template_file_path}' does not exist." + ) + print( + f"The XSD template file '{xsd_template_file_path}' does not exist." ) - print("The XSD template file '{xsd_file_path}' does not exist.") sys.exit(1) if not os.path.isfile(data_file_path): @@ -180,18 +184,20 @@ def main( sys.exit(1) # Validate the XML file and raise a SystemExit exception if invalid - if not validate_via_xsd(xml_file_path, xsd_file_path): - logger.info( - f"Error: XML located at {xml_file_path} is invalid." + is_valid = validate_via_xsd( + xml_template_file_path, xsd_template_file_path + ) + if not is_valid: + logger.error( + f"Error: XML located at {xml_template_file_path} is invalid." ) sys.exit(1) process_files( xml_message_type, - xml_file_path, - xsd_file_path, + xml_template_file_path, + xsd_template_file_path, data_file_path, - output_file_path, ) diff --git a/pain001/context/context.py b/pain001/context/context.py index ef6a761..0b625a8 100644 --- a/pain001/context/context.py +++ b/pain001/context/context.py @@ -75,27 +75,33 @@ def set_log_level(self, log_level): Raises: Exception: If the log level is invalid. """ + valid_log_levels = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, + } + if isinstance( log_level, int ): # Check if log_level is an integer - self.log_level = log_level + if log_level in valid_log_levels.values(): + self.log_level = log_level + else: + raise Exception("Invalid log level") else: log_level = ( log_level.strip().upper() ) # Strip and convert to uppercase - if log_level == "DEBUG": - self.log_level = logging.DEBUG - elif log_level == "INFO": - self.log_level = logging.INFO - elif log_level == "WARNING": - self.log_level = logging.WARNING - elif log_level == "ERROR": - self.log_level = logging.ERROR - elif log_level == "CRITICAL": - self.log_level = logging.CRITICAL + if log_level in valid_log_levels: + self.log_level = valid_log_levels[log_level] else: raise Exception("Invalid log level") + if self.logger: + self.logger.setLevel(self.log_level) + def init_logger(self): """Initializes the logger. diff --git a/pain001/core/__init__.py b/pain001/core/__init__.py new file mode 100644 index 0000000..f66c4d6 --- /dev/null +++ b/pain001/core/__init__.py @@ -0,0 +1,14 @@ +# Copyright (C) 2023 Sebastien Rousseau. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/pain001/core.py b/pain001/core/core.py similarity index 71% rename from pain001/core.py rename to pain001/core/core.py index d4cff4f..4ab6809 100644 --- a/pain001/core.py +++ b/pain001/core/core.py @@ -20,7 +20,7 @@ # Import the pain001 library functions from pain001.constants.constants import valid_xml_types -from pain001.context import context +from pain001.context.context import Context from pain001.csv.load_csv_data import load_csv_data from pain001.csv.validate_csv_data import validate_csv_data from pain001.db.load_db_data import load_db_data @@ -31,10 +31,9 @@ def process_files( xml_message_type, - xml_file_path, - xsd_file_path, + xml_template_file_path, + xsd_template_file_path, data_file_path, - output_file_path, ): """ This function generates an ISO 20022 payment message from a CSV or SQLite @@ -43,11 +42,10 @@ def process_files( Args: xml_message_type (str): The type of XML message to generate. Valid options are 'pain.001.001.03' and 'pain.001.001.09'. - xml_file_path (str): The path of the XML template file. - xsd_file_path (str): The path of the XSD schema file. + xml_template_file_path (str): The path of the XML template file. + xsd_template_file_path (str): The path of the XSD schema file. data_file_path (str): The path of the CSV or SQLite file containing the payment data. - output_file_path (str): The path of the output XML file. Returns: None @@ -56,11 +54,11 @@ def process_files( ValueError: If the XML message type is not supported. FileNotFoundError: If the XML template file does not exist. FileNotFoundError: If the XSD schema file does not exist. - FileNotFoundError: If the data file does not exist. + FileNotFoundError: If the Data file does not exist. """ # Initialize the context and log a message. - logger = context.Context.get_instance().get_logger() + logger = Context.get_instance().get_logger() # Loop through the payment initiation message types and check if the XML # message type is supported. @@ -72,18 +70,14 @@ def process_files( raise ValueError(error_message) # Check if the XML template file exists - if not os.path.exists(xml_file_path): - error_message = ( - f"Error: XML template '{xml_file_path}' does not exist." - ) + if not os.path.exists(xml_template_file_path): + error_message = f"Error: XML template '{xml_template_file_path}' does not exist." logger.error(error_message) raise FileNotFoundError(error_message) # Check if the XSD schema file exists - if not os.path.exists(xsd_file_path): - error_message = ( - f"Error: XSD schema file '{xsd_file_path}' does not exist." - ) + if not os.path.exists(xsd_template_file_path): + error_message = f"Error: XSD schema file '{xsd_template_file_path}' does not exist." logger.error(error_message) raise FileNotFoundError(error_message) @@ -112,53 +106,57 @@ def process_files( # Load data into a list of dictionaries based on the file type if is_csv: data = load_csv_data(data_file_path) - validate_csv_data + if not validate_csv_data(data): + error_message = "Error: Invalid CSV data." + logger.error(error_message) + raise ValueError(error_message) elif is_sqlite: data = load_db_data(data_file_path, table_name="pain001") + if not validate_db_data(data): + error_message = "Error: Invalid SQLite data." + logger.error(error_message) + raise ValueError(error_message) else: error_message = "Error: Unsupported data file type." logger.error(error_message) raise ValueError(error_message) - # Validate the data - if not validate_db_data(data): - error_message = "Error: Invalid data." - logger.error(error_message) - raise ValueError(error_message) - # Register the namespace prefixes and URIs for the XML message type register_namespaces(xml_message_type) # Generate the updated XML file path xml_generator( - data, mapping, xml_message_type, xml_file_path, xsd_file_path + data, + mapping, + xml_message_type, + xml_template_file_path, + xsd_template_file_path, ) - # Log a message - logger.info( - f"Generating XML file '{output_file_path}'" - f"from data file '{data_file_path}'." - ) - logger.info( - f"Successfully generated XML file '{output_file_path}'." - ) + # Confirm the XML file has been created + if os.path.exists(xml_template_file_path): + logger.info( + f"Successfully generated XML file '{xml_template_file_path}'" + ) + else: + logger.error( + f"Failed to generate XML file at '{xml_template_file_path}'" + ) if __name__ == "__main__": - if len(sys.argv) < 6: + if len(sys.argv) < 5: print( "Usage: python3 -m pain001 " + " ".join( [ "", - "", - "", + "", + "", "", - "", ] ) ) + sys.exit(1) - process_files( - sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5] - ) + process_files(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) diff --git a/pain001/xml/create_root_element.py b/pain001/xml/create_root_element.py index d428de1..1eaff8c 100644 --- a/pain001/xml/create_root_element.py +++ b/pain001/xml/create_root_element.py @@ -20,7 +20,6 @@ def create_root_element(payment_initiation_message_type): - # Create the namespace for the payment initiation message type. namespace = ( "urn:iso:std:iso:20022:tech:xsd:" @@ -34,9 +33,7 @@ def create_root_element(payment_initiation_message_type): # Set the schema location. schema_location = ( - namespace + " " - + payment_initiation_message_type - + ".xsd" + namespace + " " + payment_initiation_message_type + ".xsd" ) root.set("xsi:schemaLocation", schema_location) diff --git a/pain001/xml/generate_updated_xml_file_path.py b/pain001/xml/generate_updated_xml_file_path.py index b9c7f64..271817b 100644 --- a/pain001/xml/generate_updated_xml_file_path.py +++ b/pain001/xml/generate_updated_xml_file_path.py @@ -20,10 +20,9 @@ def generate_updated_xml_file_path( - xml_file_path, - payment_initiation_message_type + xml_file_path, payment_initiation_message_type ): - print(os.path.splitext(xml_file_path)[0]) + # print(os.path.splitext(xml_file_path)[0]) base_directory = os.path.dirname(xml_file_path) base_name = os.path.basename(xml_file_path) file_name, _ = os.path.splitext(base_name) diff --git a/pain001/xml/generate_xml.py b/pain001/xml/generate_xml.py index d435ac8..73e64a2 100644 --- a/pain001/xml/generate_xml.py +++ b/pain001/xml/generate_xml.py @@ -27,7 +27,7 @@ def create_common_elements(parent, row, mapping): def create_xml_v3(root, data, mapping): - print("XML v3") + # print("XML v3") # Create CstmrCdtTrfInitn element cstmr_cdt_trf_initn_element = ET.Element("CstmrCdtTrfInitn") @@ -41,9 +41,7 @@ def create_xml_v3(root, data, mapping): for xml_tag, csv_column in mapping.items(): if xml_tag in ["MsgId", "CreDtTm", "NbOfTxs"]: create_xml_element( - GrpHdr_element, - xml_tag, - data[0][csv_column] + GrpHdr_element, xml_tag, data[0][csv_column] ) # Create new "InitgPty" element in the XML tree using data from the @@ -74,9 +72,7 @@ def create_xml_v3(root, data, mapping): # Create new "BtchBookg" element in the XML tree using data # from the CSV file create_xml_element( - PmtInf_element, - "BtchBookg", - row["batch_booking"].lower() + PmtInf_element, "BtchBookg", row["batch_booking"].lower() ) # Create new "NbOfTxs" element in the XML tree using data from @@ -86,9 +82,7 @@ def create_xml_v3(root, data, mapping): # Create new "CtrlSum" element in the XML tree using data from # the CSV file create_xml_element( - PmtInf_element, - "CtrlSum", - f"{row['control_sum']}" + PmtInf_element, "CtrlSum", f"{row['control_sum']}" ) # Create new "PmtTpInf" element in the XML tree using data from @@ -106,7 +100,7 @@ def create_xml_v3(root, data, mapping): create_xml_element( PmtInf_element, "ReqdExctnDt", - row["requested_execution_date"] + row["requested_execution_date"], ) # Create new "Dbtr" element in the XML tree using data from @@ -214,7 +208,9 @@ def create_xml_v9(root, data, mapping): # Add the MsgId, CreDtTm, and NbOfTxs elements to the GrpHdr element for xml_tag, csv_column in mapping.items(): if xml_tag in ["MsgId", "CreDtTm", "NbOfTxs"]: - create_xml_element(GrpHdr_element, xml_tag, data[0][csv_column]) + create_xml_element( + GrpHdr_element, xml_tag, data[0][csv_column] + ) # Create new "InitgPty" element in the XML tree using data from the # CSV file @@ -265,7 +261,8 @@ def create_xml_v9(root, data, mapping): # replace with the appropriate value child_element2.text = row["debtor_agent_BIC"] child_element2.set( - "xmlns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09") + "xmlns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" + ) child_element.append(child_element2) DbtrAgt_element.append(child_element) PmtInf_element.append(DbtrAgt_element) @@ -306,7 +303,8 @@ def create_xml_v9(root, data, mapping): # replace with the appropriate value child_element2.text = row["creditor_agent_BIC"] child_element2.set( - "xmlns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09") + "xmlns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" + ) child_element.append(child_element2) CdtrAgt_element.append(child_element) CdtTrfTxInf_element.append(CdtrAgt_element) diff --git a/pain001/xml/validate_via_xsd.py b/pain001/xml/validate_via_xsd.py index 197d86f..d3cd100 100644 --- a/pain001/xml/validate_via_xsd.py +++ b/pain001/xml/validate_via_xsd.py @@ -15,19 +15,38 @@ # See the License for the specific language governing permissions and # limitations under the License. -import xml.etree.ElementTree as ET - -# Validate XML file against XSD schema using xmlschema package -# (https://pypi.org/project/xmlschema/) and ElementTree package -# (https://docs.python.org/3/library/xml.etree.elementtree.html) +import defusedxml.ElementTree as ET +import xmlschema def validate_via_xsd(xml_file_path, xsd_file_path): - # Load XML and XSD files - xml_tree = ET.parse(xml_file_path) + """ + Validates an XML file against an XSD schema. + + Args: + xml_file_path (str): Path to the XML file to validate. + xsd_file_path (str): Path to the XSD schema file. + + Returns: + bool: True if the XML file is valid, False otherwise. + """ + + # Load XML file into an ElementTree object. + try: + xml_tree = ET.parse(xml_file_path) + except Exception as e: + print(f"Error: {e}") + return False + + # Load XSD schema into an XMLSchema object. xsd = xmlschema.XMLSchema(xsd_file_path) - # Validate XML file against XSD schema - is_valid = xsd.is_valid(xml_tree) + # Validate XML file against XSD schema. + try: + is_valid = xsd.is_valid(xml_tree) + except Exception as e: + print(f"Error: {e}") + return False + # Return True if XML file is valid, False otherwise. return is_valid diff --git a/pain001/xml/xml_generator.py b/pain001/xml/xml_generator.py index 5854997..2b90c4b 100644 --- a/pain001/xml/xml_generator.py +++ b/pain001/xml/xml_generator.py @@ -69,20 +69,23 @@ def xml_generator( # Write the updated XML tree to a file write_xml_to_file(updated_xml_file_path, root) + print( + f"A new XML file has been created at {updated_xml_file_path}" + ) + # Validate the updated XML file against the XSD schema is_valid = validate_via_xsd( updated_xml_file_path, xsd_file_path ) if not is_valid: - print("❌ Error: Invalid XML data.") + print("Error: Invalid XML data.") sys.exit(1) else: - print(f"❯ XML located at {updated_xml_file_path} is valid.") + print(f"The XML has been validated against {xsd_file_path}") else: # Handle the case when the payment_initiation_message_type is # not valid print( - "❌", "Error: Invalid XML message type:", payment_initiation_message_type, ) diff --git a/pyproject.toml b/pyproject.toml index 07cff7b..78ec1e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pain001" -version = "0.0.19" +version = "0.0.20" description = "Pain001 is a Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data." authors = ["Sebastien Rousseau "] license = "Apache Software License" diff --git a/setup.cfg b/setup.cfg index b496816..ba18af6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pain001 -version = 0.0.19 +version = 0.0.20 author = Sebastian Rousseau author_email = sebastian.rousseau@gmail.com description = pain001 is a Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data. diff --git a/setup.py b/setup.py index 92027ea..55b269a 100644 --- a/setup.py +++ b/setup.py @@ -176,7 +176,7 @@ TEST_DEPENDENCIES = ["xmlschema>=2.3.0", "pytest>=7.3.1"] -VERSION = "0.0.19" +VERSION = "0.0.20" URL = "https://github.com/sebastienrousseau/pain001" diff --git a/templates/pain.001.001.03/template.db b/templates/pain.001.001.03/template.db new file mode 100644 index 0000000000000000000000000000000000000000..ab37c3259a5259dd24fc31165cd352d143beaab6 GIT binary patch literal 8192 zcmeI0U2oeq6owr)#V~AH1{6Uz1jX0}U00;(NJ`X)u>n(JIdGQ_sjY0mZUkCpVqp@Q z$#T+M1{knEydSV5Sv2f?>}r=3ERd$e!}oc4XyADG;V3NyI#Wf?OJudaY_;31pAc%b zTI+DP;nvq9=u~6+TGO5XXxCeB<7XT2!rI0^E%=zrIA9zw4j2cF1I7X4fN{V$U>qqYZl;f)ZvMFWl04bk+8QMS;^6RE@x*vDQ5)~FCd+1 zk&c5YB{ryhh@9GvN8G#Qhdj?YI#TE9v`iBqXVxMp!xa0T>o84T3+x4Tb942mBHx|w z_xBC?YuImVK~5qjhOP|3?#~*LLK-qcnGv|kleGm ihnX7j3`HWDbVJ93a10X{5(R*K>@#0;)(PGV>gHb&;Q&ei literal 0 HcmV?d00001 diff --git a/templates/pain.001.001.03/template.xml b/templates/pain.001.001.03/template.xml index 7156a03..2313743 100644 --- a/templates/pain.001.001.03/template.xml +++ b/templates/pain.001.001.03/template.xml @@ -34,6 +34,16 @@ + + BANKXXXXX + Bank Name + + Country Code + Postal Code + Street Name + Building Number + Town Name + {% for tx in transactions %} diff --git a/tests/test_context.py b/tests/test_context.py index 9aa1bec..579c721 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -6,48 +6,85 @@ class TestContext(unittest.TestCase): def setUp(self): - self.context = Context.get_instance() - self.context.set_name("test_context") + Context.instance = None def tearDown(self): - self.context.logger = None + if Context.instance: + Context.instance.logger = None Context.instance = None - def test_get_instance(self): - # Test that the first call to get_instance() creates a new instance - # of the class. - first_instance = Context.get_instance() - self.assertIsNotNone(first_instance) - - # Test that subsequent calls to get_instance() return the same - # instance. - second_instance = Context.get_instance() - self.assertEqual(first_instance, second_instance) + def test_singleton(self): + # Test that Context is a singleton + context1 = Context() + context2 = Context.get_instance() + self.assertEqual(context1, context2) + with self.assertRaises(Exception): + Context() def test_set_name(self): # Test that set_name() sets the name of the logger. - context = self.context + context = Context.get_instance() context.set_name("my_context") self.assertEqual(context.name, "my_context") def test_set_log_level(self): # Test that set_log_level() sets the log level of the logger. - context = self.context - context.set_log_level(logging.DEBUG) - self.assertEqual(context.log_level, logging.DEBUG) + context = Context.get_instance() + + # Test all valid log levels + valid_log_levels = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, + } + + for level_str, level_int in valid_log_levels.items(): + context.set_log_level(level_str) + self.assertEqual(context.log_level, level_int) + context.set_log_level(level_int) + self.assertEqual(context.log_level, level_int) # Test that set_log_level() raises an exception if the log level is # invalid. with self.assertRaises(Exception): context.set_log_level("INVALID") + with self.assertRaises(Exception): + context.set_log_level(12345) # some invalid int + + def test_init_logger(self): + # Test that init_logger() initializes the logger. + context = Context.get_instance() + + # Ensure the logger is not initialized + context.logger = None + context.init_logger() + self.assertIsNotNone(context.logger) + + # Test that init_logger() raises an exception if called again. + with self.assertRaises(Exception): + context.init_logger() def test_get_logger(self): # Test that get_logger() returns the logger. - context = self.context + context = Context.get_instance() + logger = context.get_logger() + self.assertIsNotNone(logger) + self.assertEqual(logger, context.logger) + + # Test that get_logger() can initialize the logger. + context.logger = None logger = context.get_logger() self.assertIsNotNone(logger) self.assertEqual(logger, context.logger) + def test_log_level_propagation(self): + # Test that the log level is correctly propagated to the logger. + context = Context.get_instance() + context.set_log_level(logging.DEBUG) + self.assertEqual(context.logger.level, logging.DEBUG) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_core.py b/tests/test_core.py index 57d8e3d..e7330a9 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5,7 +5,7 @@ from contextlib import contextmanager from io import StringIO -from pain001.core import process_files +from pain001.core.core import process_files @contextmanager @@ -18,13 +18,6 @@ def catch_stdout(): class TestProcessFiles: - @pytest.fixture(autouse=True) - def setup_teardown(self): - self.output_file_path = "tests/data/pain.001.001.03.xml" - yield - if os.path.exists(self.output_file_path): - os.remove(self.output_file_path) - def test_invalid_csv_data(self): """ Test case for processing files with invalid CSV data. @@ -36,7 +29,6 @@ def test_invalid_csv_data(self): "tests/data/template.xml", "tests/data/template.xsd", "tests/data/invalid.csv", - self.output_file_path, ) assert exc_info.value.code == 1 @@ -51,7 +43,6 @@ def test_invalid_xml_message_type(self): "tests/data/template.xml", "tests/data/template.xsd", "tests/data/template.csv", - self.output_file_path, ) error_message = str(exc_info.value) @@ -70,7 +61,6 @@ def test_nonexistent_data_file_path(self): "tests/data/template.xml", "tests/data/template.xsd", "tests/data/nonexistent.csv", - self.output_file_path, ) assert ( str(exc_info.value) @@ -87,7 +77,6 @@ def test_nonexistent_xml_file_path(self): "tests/data/nonexistent.xml", "tests/data/template.xsd", "tests/data/template.csv", - self.output_file_path, ) # assert exc_info.value.code == 1 @@ -101,7 +90,6 @@ def test_nonexistent_xsd_file_path(self): "tests/data/template.xml", "tests/data/nonexistent.xsd", "tests/data/template.csv", - self.output_file_path, ) # assert exc_info.value.code == 1 @@ -114,13 +102,8 @@ def test_successful_execution(self): "tests/data/template.xml", "tests/data/template.xsd", "tests/data/template.csv", - self.output_file_path, ) - output_file_exists = os.path.exists(self.output_file_path) - if not output_file_exists: - raise AssertionError("Output file does not exist.") - def test_unsupported_data_file_type(self): """ Test case for processing files with an unsupported data file type. @@ -131,7 +114,6 @@ def test_unsupported_data_file_type(self): "tests/data/template.xml", "tests/data/template.xsd", "tests/data/invalid.rtf", - self.output_file_path, ) assert ( str(exc_info.value) == "Error: Unsupported data file type." @@ -151,11 +133,7 @@ def test_uses_sqlite_database(self): xml_file_path, xsd_file_path, data_file_path, - self.output_file_path, ) - output_file_exists = os.path.exists(self.output_file_path) - if not output_file_exists: - raise AssertionError("Output file does not exist.") def test_valid_xml_message_type(self): """ @@ -166,8 +144,4 @@ def test_valid_xml_message_type(self): "tests/data/template.xml", "tests/data/template.xsd", "tests/data/template.csv", - self.output_file_path, ) - output_file_exists = os.path.exists(self.output_file_path) - if not output_file_exists: - raise AssertionError("Output file does not exist.") diff --git a/tests/test_main.py b/tests/test_main.py index 7db484a..8699666 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,79 +1,156 @@ -import io -import pytest -import sys -from pain001.__main__ import main from unittest.mock import patch +from click.testing import CliRunner +from pain001.__main__ import main class TestMain: def setup_method(self): + self.runner = CliRunner() self.xml_message_type = "pain.001.001.03" self.xml_file = "tests/data/template.xml" self.xsd_file = "tests/data/template.xsd" self.csv_file = "tests/data/template.csv" - self.output_file = "tests/data/output.xml" def test_main_with_valid_files(self): - with patch.object( - sys, - "argv", + result = self.runner.invoke( + main, [ - "", + "--xml_message_type", self.xml_message_type, + "--xml_template_file_path", self.xml_file, + "--xsd_template_file_path", self.xsd_file, + "--data_file_path", + self.csv_file, + ], + ) + assert ( + "The XML has been validated against tests/data/template.xsd\n" + in result.output + ) + assert result.exit_code == 0 + + def test_main_with_missing_xml_message_type(self): + result = self.runner.invoke( + main, + [ + "--xml_template_file_path", + self.xml_file, + "--xsd_template_file_path", + self.xsd_file, + "--data_file_path", + self.csv_file, + ], + ) + assert result.exit_code == 1 + assert "Error: xml_message_type is required." in result.output + + def test_main_with_missing_xsd_template_file(self): + result = self.runner.invoke( + main, + [ + "--xml_message_type", + self.xml_message_type, + "--xml_template_file_path", + self.xml_file, + "--data_file_path", self.csv_file, - self.output_file, ], - ): - with patch( - "sys.stdout", new_callable=io.StringIO - ) as captured_output: - main() - assert ( - "❯ XML located at tests/data/pain.001.001.03.xml is valid." - in captured_output.getvalue() - ) + ) + assert result.exit_code == 1 + assert ( + "Error: xsd_template_file_path is required." + in result.output + ) - def test_main_with_invalid_csv_file(self): - with patch.object( - sys, - "argv", + def test_main_with_missing_data_file(self): + result = self.runner.invoke( + main, [ - "", + "--xml_message_type", self.xml_message_type, + "--xml_template_file_path", self.xml_file, + "--xsd_template_file_path", self.xsd_file, - "tests/data/invalid.csv", - self.output_file, ], - ): - with patch( - "sys.stdout", new_callable=io.StringIO - ) as captured_output: - with pytest.raises(SystemExit) as exc_info: - main() + ) + assert result.exit_code == 1 + assert "Error: data_file_path is required." in result.output - assert exc_info.type == SystemExit - assert exc_info.value.code == 1 - expected_error_message = "Error: No data to process." - assert expected_error_message in captured_output.getvalue() + def test_main_with_invalid_xml_message_type(self): + result = self.runner.invoke( + main, + [ + "--xml_message_type", + "invalid", + "--xml_template_file_path", + self.xml_file, + "--xsd_template_file_path", + self.xsd_file, + "--data_file_path", + self.csv_file, + ], + ) + assert result.exit_code == 1 + assert "Invalid XML message type: invalid." in result.output - def test_main_with_invalid_xml_file(self): - with pytest.raises(SystemExit) as exc_info: - with patch.object( - sys, - "argv", - [ - "", - self.xml_message_type, - "tests/data/invalid.xml", - self.xsd_file, - self.csv_file, - self.output_file, - ], - ): - main() + def test_main_with_invalid_xml_template_file(self): + result = self.runner.invoke( + main, + [ + "--xml_message_type", + self.xml_message_type, + "--xml_template_file_path", + "invalid", + "--xsd_template_file_path", + self.xsd_file, + "--data_file_path", + self.csv_file, + ], + ) + assert result.exit_code == 1 + assert ( + "The XML template file 'invalid' does not exist." + in result.output + ) - assert exc_info.type == SystemExit - assert exc_info.value.code == 1 + def test_main_with_invalid_xsd_template_file(self): + result = self.runner.invoke( + main, + [ + "--xml_message_type", + self.xml_message_type, + "--xml_template_file_path", + self.xml_file, + "--xsd_template_file_path", + "invalid", + "--data_file_path", + self.csv_file, + ], + ) + assert result.exit_code == 1 + assert ( + "The XSD template file 'invalid' does not exist." + in result.output + ) + + def test_main_with_invalid_data_file(self): + result = self.runner.invoke( + main, + [ + "--xml_message_type", + self.xml_message_type, + "--xml_template_file_path", + self.xml_file, + "--xsd_template_file_path", + self.xsd_file, + "--data_file_path", + "invalid", + ], + ) + assert result.exit_code == 1 + assert ( + "The data file 'invalid' does not exist." in result.output + ) From 0dbfac42831a1b62c86762c0cf4e0c253b647019 Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Tue, 13 Jun 2023 16:04:17 +0100 Subject: [PATCH 02/11] fix(pain001): lint issues and updated unit tests documentation --- pain001/core/core.py | 10 ++++-- pain001/xml/validate_via_xsd.py | 1 - tests/test_context.py | 30 ++++++++++------- tests/test_create_xml_element.py | 57 ++++++++++++++++++++------------ tests/test_generate_xml.py | 13 ++++++++ tests/test_main.py | 1 - tests/test_validate_via_xsd.py | 46 ++++++++++++++++++-------- tests/test_xml_generator.py | 5 +++ 8 files changed, 112 insertions(+), 51 deletions(-) diff --git a/pain001/core/core.py b/pain001/core/core.py index 4ab6809..3d31118 100644 --- a/pain001/core/core.py +++ b/pain001/core/core.py @@ -71,13 +71,19 @@ def process_files( # Check if the XML template file exists if not os.path.exists(xml_template_file_path): - error_message = f"Error: XML template '{xml_template_file_path}' does not exist." + error_message = ( + f"Error: XML template '{xml_template_file_path}' " + f"does not exist." + ) logger.error(error_message) raise FileNotFoundError(error_message) # Check if the XSD schema file exists if not os.path.exists(xsd_template_file_path): - error_message = f"Error: XSD schema file '{xsd_template_file_path}' does not exist." + error_message = ( + f"Error: XSD schema file '{xsd_template_file_path}' " + f"does not exist." + ) logger.error(error_message) raise FileNotFoundError(error_message) diff --git a/pain001/xml/validate_via_xsd.py b/pain001/xml/validate_via_xsd.py index d3cd100..0ee147e 100644 --- a/pain001/xml/validate_via_xsd.py +++ b/pain001/xml/validate_via_xsd.py @@ -16,7 +16,6 @@ # limitations under the License. import defusedxml.ElementTree as ET -import xmlschema def validate_via_xsd(xml_file_path, xsd_file_path): diff --git a/tests/test_context.py b/tests/test_context.py index 579c721..507b1b1 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -5,16 +5,20 @@ class TestContext(unittest.TestCase): + """Unit tests for the Context class.""" + def setUp(self): + """Set up the test fixture.""" Context.instance = None def tearDown(self): + """Tear down the test fixture.""" if Context.instance: Context.instance.logger = None Context.instance = None def test_singleton(self): - # Test that Context is a singleton + """Test that Context is a singleton.""" context1 = Context() context2 = Context.get_instance() self.assertEqual(context1, context2) @@ -22,16 +26,16 @@ def test_singleton(self): Context() def test_set_name(self): - # Test that set_name() sets the name of the logger. + """Test that set_name() sets the name of the logger.""" context = Context.get_instance() context.set_name("my_context") self.assertEqual(context.name, "my_context") def test_set_log_level(self): - # Test that set_log_level() sets the log level of the logger. + """Test that set_log_level() sets the log level of the logger""" context = Context.get_instance() - # Test all valid log levels + """Test all valid log levels""" valid_log_levels = { "DEBUG": logging.DEBUG, "INFO": logging.INFO, @@ -46,41 +50,43 @@ def test_set_log_level(self): context.set_log_level(level_int) self.assertEqual(context.log_level, level_int) - # Test that set_log_level() raises an exception if the log level is - # invalid. + """ + Test that set_log_level() raises an exception if the log level is + invalid. + """ with self.assertRaises(Exception): context.set_log_level("INVALID") with self.assertRaises(Exception): context.set_log_level(12345) # some invalid int def test_init_logger(self): - # Test that init_logger() initializes the logger. + """Test that init_logger() initializes the logger.""" context = Context.get_instance() - # Ensure the logger is not initialized + """Ensure the logger is not initialized""" context.logger = None context.init_logger() self.assertIsNotNone(context.logger) - # Test that init_logger() raises an exception if called again. + """Test that init_logger() raises an exception if called again.""" with self.assertRaises(Exception): context.init_logger() def test_get_logger(self): - # Test that get_logger() returns the logger. + """Test that get_logger() returns the logger.""" context = Context.get_instance() logger = context.get_logger() self.assertIsNotNone(logger) self.assertEqual(logger, context.logger) - # Test that get_logger() can initialize the logger. + """Test that get_logger() can initialize the logger.""" context.logger = None logger = context.get_logger() self.assertIsNotNone(logger) self.assertEqual(logger, context.logger) def test_log_level_propagation(self): - # Test that the log level is correctly propagated to the logger. + """Test that the log level is correctly propagated to the logger.""" context = Context.get_instance() context.set_log_level(logging.DEBUG) self.assertEqual(context.logger.level, logging.DEBUG) diff --git a/tests/test_create_xml_element.py b/tests/test_create_xml_element.py index 91f3182..0c9f1c4 100644 --- a/tests/test_create_xml_element.py +++ b/tests/test_create_xml_element.py @@ -6,40 +6,53 @@ class TestCreateXmlElement(unittest.TestCase): - def test_create_element_with_tag_only(self): - root = ET.Element('root') - elem = create_xml_element(root, 'test') - self.assertEqual(elem.tag, 'test') + """ + Test if the XML element is created correctly with a tag only. + """ + root = ET.Element("root") + elem = create_xml_element(root, "test") + self.assertEqual(elem.tag, "test") self.assertIsNone(elem.text) - self.assertEqual(root.find('test'), elem) + self.assertEqual(root.find("test"), elem) def test_create_element_with_tag_and_text(self): - root = ET.Element('root') - elem = create_xml_element(root, 'test', text='Hello, world!') - self.assertEqual(elem.tag, 'test') - self.assertEqual(elem.text, 'Hello, world!') - self.assertEqual(root.find('test'), elem) + """ + Test if the XML element is created correctly with a tag and text. + """ + root = ET.Element("root") + elem = create_xml_element(root, "test", text="Hello, world!") + self.assertEqual(elem.tag, "test") + self.assertEqual(elem.text, "Hello, world!") + self.assertEqual(root.find("test"), elem) def test_create_element_with_tag_and_attributes(self): - root = ET.Element('root') - attributes = {'attr1': 'value1', 'attr2': 'value2'} - elem = create_xml_element(root, 'test', attributes=attributes) - self.assertEqual(elem.tag, 'test') + """ + Test if the XML element is created correctly with a tag and attributes. + """ + root = ET.Element("root") + attributes = {"attr1": "value1", "attr2": "value2"} + elem = create_xml_element(root, "test", attributes=attributes) + self.assertEqual(elem.tag, "test") self.assertIsNone(elem.text) self.assertEqual(elem.attrib, attributes) - self.assertEqual(root.find('test'), elem) + self.assertEqual(root.find("test"), elem) def test_create_element_with_tag_text_and_attributes(self): - root = ET.Element('root') - attributes = {'attr1': 'value1', 'attr2': 'value2'} + """ + Test if the XML element is created correctly with a tag, text and + attributes. + """ + root = ET.Element("root") + attributes = {"attr1": "value1", "attr2": "value2"} elem = create_xml_element( - root, 'test', text='Hello, world!', attributes=attributes) - self.assertEqual(elem.tag, 'test') - self.assertEqual(elem.text, 'Hello, world!') + root, "test", text="Hello, world!", attributes=attributes + ) + self.assertEqual(elem.tag, "test") + self.assertEqual(elem.text, "Hello, world!") self.assertEqual(elem.attrib, attributes) - self.assertEqual(root.find('test'), elem) + self.assertEqual(root.find("test"), elem) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_generate_xml.py b/tests/test_generate_xml.py index 559d4a6..96e1a06 100644 --- a/tests/test_generate_xml.py +++ b/tests/test_generate_xml.py @@ -11,6 +11,9 @@ class TestXMLCreation(unittest.TestCase): def setUp(self): + """ + Test setup + """ self.root = ET.Element("Root") self.row = { "initiator_name": "Initiator", @@ -40,6 +43,9 @@ def setUp(self): } def test_create_common_elements(self): + """ + Test create_common_elements + """ create_common_elements(self.root, self.row, self.mapping) self.assertEqual(len(self.root), 2) self.assertEqual(self.root[0].tag, "PmtInfId") @@ -48,6 +54,9 @@ def test_create_common_elements(self): self.assertEqual(self.root[1].text, "pain.001.001.09") def test_create_xml_v3(self): + """ + Test create_xml_v3 + """ create_xml_v3(self.root, [self.row], self.mapping) cstmr_cdt_trf_initn_element = self.root[0] self.assertEqual( @@ -56,6 +65,10 @@ def test_create_xml_v3(self): # You can continue to assert more conditions based on your expectations def test_create_xml_v9(self): + """ + Test create_xml_v9 + """ + create_xml_v9(self.root, [self.row], self.mapping) cstmr_cdt_trf_initn_element = self.root[0] self.assertEqual( diff --git a/tests/test_main.py b/tests/test_main.py index 8699666..4c58ca1 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,4 +1,3 @@ -from unittest.mock import patch from click.testing import CliRunner from pain001.__main__ import main diff --git a/tests/test_validate_via_xsd.py b/tests/test_validate_via_xsd.py index 39e428b..5b727ef 100644 --- a/tests/test_validate_via_xsd.py +++ b/tests/test_validate_via_xsd.py @@ -6,29 +6,36 @@ class TestValidateViaXsd(unittest.TestCase): - def setUp(self): - self.valid_xml_file = 'valid_test.xml' - self.invalid_xml_file = 'invalid_test.xml' - self.xsd_file = 'test_schema.xsd' + """ + Test case setup method. + """ + self.valid_xml_file = "valid_test.xml" + self.invalid_xml_file = "invalid_test.xml" + self.xsd_file = "test_schema.xsd" # Create valid XML test file - with open(self.valid_xml_file, 'w') as f: - f.write(''' + with open(self.valid_xml_file, "w") as f: + f.write( + """ Valid data - ''') + """ + ) # Create invalid XML test file - with open(self.invalid_xml_file, 'w') as f: - f.write(''' + with open(self.invalid_xml_file, "w") as f: + f.write( + """ Invalid data - ''') + """ + ) # Create test XSD schema file - with open(self.xsd_file, 'w') as f: - f.write(''' + with open(self.xsd_file, "w") as f: + f.write( + """ @@ -42,17 +49,30 @@ def setUp(self): - ''') + """ + ) def tearDown(self): + """ + Test case tear down method. + """ os.remove(self.valid_xml_file) os.remove(self.invalid_xml_file) os.remove(self.xsd_file) def test_valid_xml(self): + """ + Test case for validating a valid XML file against an XSD schema. + """ assert validate_via_xsd(self.valid_xml_file, self.xsd_file) def test_invalid_xml(self): + """ + Test case for validating an invalid XML file against an XSD schema. + """ + assert not validate_via_xsd( + self.invalid_xml_file, self.xsd_file + ) assert not validate_via_xsd( self.invalid_xml_file, self.xsd_file ) diff --git a/tests/test_xml_generator.py b/tests/test_xml_generator.py index 48f3dc1..91f8c42 100644 --- a/tests/test_xml_generator.py +++ b/tests/test_xml_generator.py @@ -5,6 +5,11 @@ class TestXmlGenerator(unittest.TestCase): def test_xml_generator_with_invalid_input(self): + """ + Test if the XML generator exits with a non-zero exit code when + invalid input is provided. + """ + # Arrange data = { "amount": "100.00", From 8a12b55f84d365877dfccff967f95a78b2ce574f Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Tue, 13 Jun 2023 16:25:29 +0100 Subject: [PATCH 03/11] fix(pain001): updated requirements.txt --- pain001/__main__.py | 16 ++++++++-------- requirements.txt | 2 ++ setup.cfg | 2 ++ setup.py | 15 ++++++++++----- tests/data/invalid.xml | 2 -- tests/test_core.py | 1 - 6 files changed, 22 insertions(+), 16 deletions(-) delete mode 100644 tests/data/invalid.xml diff --git a/pain001/__main__.py b/pain001/__main__.py index d93d239..dd99295 100644 --- a/pain001/__main__.py +++ b/pain001/__main__.py @@ -184,14 +184,14 @@ def check_variable(variable, name): sys.exit(1) # Validate the XML file and raise a SystemExit exception if invalid - is_valid = validate_via_xsd( - xml_template_file_path, xsd_template_file_path - ) - if not is_valid: - logger.error( - f"Error: XML located at {xml_template_file_path} is invalid." - ) - sys.exit(1) + # is_valid = validate_via_xsd( + # xml_template_file_path, xsd_template_file_path + # ) + # if not is_valid: + # logger.error( + # f"Error: XML located at {xml_template_file_path} is invalid." + # ) + # sys.exit(1) process_files( xml_message_type, diff --git a/requirements.txt b/requirements.txt index 45cae35..0aa9872 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ +click==8.1.3 +defusedxml==0.7.1 xmlschema==2.3.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index ba18af6..2c2b4d9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,6 +35,8 @@ packages = pain001 install_requires = + click + defusedxml xmlschema [wheel] diff --git a/setup.py b/setup.py index 55b269a..edcf023 100644 --- a/setup.py +++ b/setup.py @@ -172,16 +172,21 @@ Pain001 is a Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data.""".strip() -DEPENDENCIES = ["xmlschema>=2.3.0"] +DEPENDENCIES = ["click==8.1.3", "defusedxml==0.7.1", "xmlschema==2.3.0"] -TEST_DEPENDENCIES = ["xmlschema>=2.3.0", "pytest>=7.3.1"] - -VERSION = "0.0.20" +TEST_DEPENDENCIES = [ + "click==8.1.3", + "defusedxml==0.7.1", + "xmlschema==2.3.0", + "pytest>=7.3.1", +] +NAME = ("pain001",) URL = "https://github.com/sebastienrousseau/pain001" +VERSION = "0.0.20" setup( - name="pain001", + name=NAME, version=VERSION, description=SHORT_DESCRIPTION, long_description=LONG_DESCRIPTION, diff --git a/tests/data/invalid.xml b/tests/data/invalid.xml deleted file mode 100644 index ff64da6..0000000 --- a/tests/data/invalid.xml +++ /dev/null @@ -1,2 +0,0 @@ - -

Hello world!

\ No newline at end of file diff --git a/tests/test_core.py b/tests/test_core.py index e7330a9..3792194 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,4 +1,3 @@ -import os import pytest import sys From 9d304ba944938d335ae1062764121ff9e21665e8 Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Tue, 13 Jun 2023 16:29:25 +0100 Subject: [PATCH 04/11] fix(pain001): deps updates --- requirements.txt | 1 + setup.cfg | 1 + setup.py | 10 ++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0aa9872..202f73b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ click==8.1.3 defusedxml==0.7.1 +rich==13.4.2 xmlschema==2.3.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 2c2b4d9..2f43c6c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,7 @@ packages = install_requires = click defusedxml + rich xmlschema [wheel] diff --git a/setup.py b/setup.py index edcf023..0090214 100644 --- a/setup.py +++ b/setup.py @@ -172,13 +172,19 @@ Pain001 is a Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data.""".strip() -DEPENDENCIES = ["click==8.1.3", "defusedxml==0.7.1", "xmlschema==2.3.0"] +DEPENDENCIES = [ + "click==8.1.3", + "defusedxml==0.7.1", + "rich==13.4.2", + "xmlschema==2.3.0", +] TEST_DEPENDENCIES = [ "click==8.1.3", "defusedxml==0.7.1", - "xmlschema==2.3.0", "pytest>=7.3.1", + "rich==13.4.2", + "xmlschema==2.3.0", ] NAME = ("pain001",) From 7c4964123b98a6809060da59d717d8556225a53f Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Tue, 13 Jun 2023 16:33:10 +0100 Subject: [PATCH 05/11] fix(pain001): commented unused function --- pain001/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pain001/__main__.py b/pain001/__main__.py index dd99295..b2ab750 100644 --- a/pain001/__main__.py +++ b/pain001/__main__.py @@ -32,7 +32,8 @@ from pain001.constants.constants import valid_xml_types from pain001.context.context import Context from pain001.core.core import process_files -from pain001.xml.validate_via_xsd import validate_via_xsd + +# from pain001.xml.validate_via_xsd import validate_via_xsd from rich.console import Console from rich.table import Table From 74275478ab6258f189132f2e3e4628de37465247 Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Wed, 14 Jun 2023 22:46:14 +0100 Subject: [PATCH 06/11] feat(pain001): updated README, deps and minor tweaks --- README.md | 282 +++++++++++++++++++++---------------------- pain001/__main__.py | 30 ++--- pain001/core/core.py | 12 +- requirements.txt | 2 +- setup.py | 4 +- tests/test_main.py | 17 ++- 6 files changed, 167 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index fa946f8..5c33ff0 100644 --- a/README.md +++ b/README.md @@ -5,111 +5,148 @@ [![PyPI][pypi-badge]][3] [![License][license-badge]][1] [![Codecov][codecov-badge]][6] -**Pain001** is a powerful Python library that enables you to create ISO 20022- -compliant payment files directly from CSV or SQLite Data Files. - ## Overview -Today, the payment industry is experiencing a rapid evolution and -transformation as it moves towards adopting **[ISO 20022][1]** as the new norm. -ISO 20022 is a global standard for sharing financial information across -organisations. It provides a harmonised protocol used by banks, corporations, -and financial institutions to automate and standardise payment transactions. - -Here are some of the benefits of using ISO 20022: - -- **Improved data quality**: ISO 20022 messages are more structured and - detailed than traditional payment messages, reducing errors and improving - efficiency. -- **Increased transparency**: ISO 20022 messages provide more information - about the payment transaction, which can help to improve the visibility and - traceability of payments. -- **Enhanced compliance**: ISO 20022 messages can help organisations follow - AML/CFT and PSD2 regulations. - -Overall, ISO 20022 is a significant improvement over traditional payment -messaging standards. It provides a more efficient, transparent, and compliant -way to process payments. - -## Pain001 in action - -The Python library **Pain001** focuses specifically on payment initiation and -advice messages, commonly known as **pain**. Payments usually start with a -**pain.001 payment initiation message**. The payer sends it to the payee (or -the payee's bank) via a secure network. This network could be **SWIFT** or -**SEPA (Single Euro Payments Area)** network, or other payment networks such -as **CHAPS**, **BACS**, **Faster Payments**, etc. The message contains the -payer's and payee's bank account details, payment amount, and other information -required to process the payment. - -**Pain001** can reduce payment processing complexity and costs by generating -ISO 20022-compliant payment files. These files automatically remove the need to -create and validate them manually, making the payment process more efficient -and cost-effective. It will save you time and resources and minimises the risk -of errors, making sure accurate and seamless payment processing. - -If you are seeking to simplify and automate your payment processing, consider -leveraging the capabilities of **Pain001**. +**Pain001** is an open-source Python Library that you can use to create +**ISO 20022-Compliant Payment Files** directly from your **CSV** or **SQLite** +Data Files. + +The Python library focuses specifically on +**Payment Initiation and Advice Messages**, commonly known as **Pain**. It is +currently designed to be compatible with the **pain.001.001.03** and +**pain.001.001.09** message types and will support more message types in the +future. + +Payments usually start with a **pain.001 payment initiation message**. The +payer sends it to the payee (or the payee’s bank) via a secure network. This +network could be **SWIFT** or **SEPA (Single Euro Payments Area) network**, or +other payment networks such as **CHAPS**, **BACS**, **Faster Payments**, etc. +The message contains the payer’s and payee’s bank account details, payment +amount, and other information required to process the payment. + +The **Pain001** library can reduce payment processing complexity and costs by +generating ISO 20022-compliant payment files. These files automatically remove +the need to create and validate them manually, making the payment process more +efficient and cost-effective. It will save you time and resources and minimises +the risk of errors, making sure accurate and seamless payment processing. + +Use the **Pain001** library to simplify, accelerate and automate your payment +processing. + +## Requirements + +**Pain001** works with macOS, Linux and Windows and requires Python 3.9.0 and +above. ## Installation -It takes just a few seconds to get up and running with **Pain001**. Open your -terminal and run the following command: +It takes just a few seconds to get up and running with **Pain001**. You can +install Pain001 from PyPI with pip or your favorite package manager: + +Open your terminal and run the following command: ```sh pip install pain001 ``` -## Usage +Add the -U switch to update to the current version, if `pain001` is already +installed. + +## Quick Start After installation, you can run **Pain001** directly from the command line. -Simply call the main function with the path of your XML template file, XSD -schema file and the path of your CSV file containing the payment data. +Simply call the main module pain001 with the paths of your + +- **XML template file** containing the various parameters you want to pass from + your Data file, +- **XSD schema file** to validate the generated XML file, and +- **Data file (CSV or SQLite)** containing the payment instructions that you + want to submit. -Once you have installed **Pain001**, you can generate and validate XML files -using the following command: +Here’s how you would do that: ```sh python3 -m pain001 \ \ - \ - \ - \ - + \ + \ + ``` -## Arguments +### Arguments -When running **Pain001**, you will need to specify four arguments: +When running **Pain001**, as briefly described above, you will need to specify +four arguments to the **Pain001** module: - `xml_message_type`: This is the type of XML message you want to generate. - Currently, the valid options are: + + The currently supported types are: - pain.001.001.03 - pain.001.001.09 -- `xml_file_path`: This is the path to the XML template file you are using. -- `xsd_file_path`: This is the path to the XSD template file you are using. -- `csv_file_path`: This is the path to the CSV data file you want to convert - to XML. -- `output_file_path`: This is the path to the output XML file you want to save - the generated XML file to. +- `xml_template_file_path`: This is the path to the XML template file you are + using that contains variables to be replaced with data from the Data file. +- `xsd_schema_file_path`: This is the path to the XSD schema file you are using + to validate the generated XML file. +- `data_file_path`: This is the path to the CSV or SQLite Data file you want to + convert to XML format. ## Examples -Here are a few example on how to use **Pain001** to generate a -pain.001.001.03 XML file from a CSV data file: +The following examples demonstrate how to use **Pain001** to generate a payment +initiation message from a CSV file and a SQLite Data file. + +### Using a CSV Data File as the source + +```sh +python3 -m pain001 \ + pain.001.001.03 \ + /path/to/your/template.xml \ + /path/to/your/pain.001.001.03.xsd \ + /path/to/your/template.csv +``` -### Via the Command Line +### Using a SQLite Data File as the source ```sh python3 -m pain001 \ pain.001.001.03 \ - /path/to/your/pain.001.001.03.xml \ + /path/to/your/template.xml \ /path/to/your/pain.001.001.03.xsd \ - /path/to/your/pain.001.001.03.csv \ - /path/to/your/output.xml + /path/to/your/template.db ``` -**Note:** The XML file that **Pain001** generates will be automatically +### Using the source code + +You can use the source code and run the example code in your +terminal/command-line. To check out the source code, clone the repository from +GitHub: + +```sh +git clone https://github.com/sebastienrousseau/pain001.git +``` + +You can then run this script to generate a payment initiation message from the +sample CSV Data file: + +```sh +python3 pain001/__main__.py \ + --xml_message_type "pain.001.001.03" \ + --xml_template_file_path "templates/pain.001.001.03/template.xml" \ + --xsd_schema_file_path "templates/pain.001.001.03/pain.001.001.03.xsd" \ + --data_file_path "templates/pain.001.001.03/template.csv" +``` + +or the sample SQLite Data File + +```sh +python3 pain001/__main__.py \ + --xml_message_type "pain.001.001.03" \ + --xml_template_file_path "templates/pain.001.001.03/template.xml" \ + --xsd_schema_file_path "templates/pain.001.001.03/pain.001.001.03.xsd" \ + --data_file_path "templates/pain.001.001.03/template.db" +``` + +> **Note:** The XML file that **Pain001** generates will automatically be validated against the XSD template file before the new XML file is saved. If the validation fails, **Pain001** will stop running and display an error message in your terminal. @@ -126,10 +163,10 @@ from pain001 import main if __name__ == '__main__': xml_message_type = 'pain.001.001.03' - xml_file_path = 'template.xml' - xsd_file_path = 'schema.xsd' - csv_file_path = 'data.csv' - main(xml_message_type, xml_file_path, xsd_file_path, csv_file_path) + xml_template_file_path = 'template.xml' + xsd_schema_file_path = 'schema.xsd' + data_file_path = 'data.csv' + main(xml_message_type, xml_template_file_path, xsd_schema_file_path, data_file_path) ``` ### Validation @@ -154,37 +191,7 @@ print(f"XML validation result: {is_valid}") ## Documentation 📖 -> ℹ️ **Info:** Do check out our [website][0] for more information. - -### Payment Messages - -The following **ISO 20022 Payment Initiation message types** are -currently supported: - -- **pain.001.001.03** - Customer Credit Transfer Initiation - -This message is used to transmit credit transfer instructions from the -originator (the party initiating the payment) to the originator's bank. The -message supports both bulk and single payment instructions, allowing for the -transmission of multiple payments in a batch or individual payments separately. -The pain.001.001.03 message format is part of the ISO 20022 standard and is -commonly used for SEPA Credit Transfers within the Single Euro Payments Area. -It includes relevant information such as the originator's and beneficiary's -details, payment amounts, payment references, and other transaction-related -information required for processing the credit transfers. - -- **pain.001.001.09** - Customer Credit Transfer Initiation - -This message format is part of the ISO 20022 standard and is commonly used for -SEPA Credit Transfers within the Single Euro Payments Area. It enables the -transmission of credit transfer instructions from the originator to the -originator's bank. The message includes essential information such as the -originator's and beneficiary's details, payment amounts, payment references, -and other transaction-related information required for processing the credit -transfers. - -More message types will be added in the future. Please refer to the section -below for more details. +> **Info:** Do check out our [website][0] for comprehensive documentation. ### Supported messages @@ -199,10 +206,10 @@ customer. | Status | Message type | Name | |---|---|---| -| ⏳ | [camt.052.001.10] | Bank-to-Customer Account Statement | -| ⏳ | [camt.060.001.10] | Customer Account Notification | -| ⏳ | [camt.054.001.10] | Customer Account Statement Request | -| ⏳ | [camt.053.001.10] | Customer Account Identification | +| ⏳ | camt.052.001.10 | Bank-to-Customer Account Statement | +| ⏳ | camt.060.001.10 | Customer Account Notification | +| ⏳ | camt.054.001.10 | Customer Account Statement Request | +| ⏳ | camt.053.001.10 | Customer Account Identification | #### Payments Clearing and Settlement @@ -211,14 +218,14 @@ settlement of payment transactions. | Status | Message type | Name | |---|---|---| -| ⏳ | [pacs.002.001.12] | Credit Transfer Notification | -| ⏳ | [pacs.003.001.09] | Direct Debit Initiation | -| ⏳ | [pacs.004.001.11] | Direct Debit Reversal | -| ⏳ | [pacs.007.001.11] | Customer Direct Debit Confirmation | -| ⏳ | [pacs.008.001.10] | Credit Transfer Initiation | -| ⏳ | [pacs.009.001.10] | Credit Transfer Reversal | -| ⏳ | [pacs.010.001.05] | Account Identification | -| ⏳ | [pacs.028.001.05] | Account Statement Request | +| ⏳ | pacs.002.001.12 | Credit Transfer Notification | +| ⏳ | pacs.003.001.09 | Direct Debit Initiation | +| ⏳ | pacs.004.001.11 | Direct Debit Reversal | +| ⏳ | pacs.007.001.11 | Customer Direct Debit Confirmation | +| ⏳ | pacs.008.001.10 | Credit Transfer Initiation | +| ⏳ | pacs.009.001.10 | Credit Transfer Reversal | +| ⏳ | pacs.010.001.05 | Account Identification | +| ⏳ | pacs.028.001.05 | Account Statement Request | #### Payments Initiation @@ -229,14 +236,14 @@ and monitor payments. | Status | Message type | Name | |---|---|---| | ✅ | [pain.001.001.03][pain.001.001.03] | Customer Credit Transfer Initiation | -| ⏳ | [pain.001.001.04][pain.001.001.04] | Customer Direct Debit Initiation | -| ⏳ | [pain.001.001.05][pain.001.001.05] | Customer Direct Debit Reversal | -| ⏳ | [pain.001.001.06][pain.001.001.06] | Customer Credit Transfer Reversal | -| ⏳ | [pain.001.001.07][pain.001.001.07] | Customer Account Notification | -| ⏳ | [pain.001.001.08][pain.001.001.08] | Customer Account Statement | +| ⏳ | pain.001.001.04 | Customer Direct Debit Initiation | +| ⏳ | pain.001.001.05 | Customer Direct Debit Reversal | +| ⏳ | pain.001.001.06 | Customer Credit Transfer Reversal | +| ⏳ | pain.001.001.07 | Customer Account Notification | +| ⏳ | pain.001.001.08 | Customer Account Statement | | ✅ | [pain.001.001.09][pain.001.001.09] | Customer Credit Transfer Initiation | -| ⏳ | [pain.001.001.10][pain.001.001.10] | Customer Account Closure Request | -| ⏳ | [pain.001.001.11][pain.001.001.11] | Customer Account Change Request | +| ⏳ | pain.001.001.10 | Customer Account Closure Request | +| ⏳ | pain.001.001.11 | Customer Account Change Request | ## License 📝 @@ -269,29 +276,10 @@ of [Pain001][5] for their help and support. [5]: https://github.com/sebastienrousseau/pain001/graphs/contributors [6]: https://codecov.io/github/sebastienrousseau/pain001?branch=main -[camt.052.001.10]: docs/bank-to-customer-cash-management/messages/camt.052.001.10/README.md -[camt.060.001.10]: docs/bank-to-customer-cash-management/messages/camt.053.001.10/README.md -[camt.054.001.10]: docs/bank-to-customer-cash-management/messages/camt.054.001.10/README.md -[camt.053.001.10]: docs/bank-to-customer-cash-management/messages/camt.053.001.10/README.md -[pacs.002.001.12]: docs/payments-clearing-and-settlement/messages/pacs.002.001.12/README.md -[pacs.003.001.09]: docs/payments-clearing-and-settlement/messages/pacs.003.001.09/README.md -[pacs.004.001.11]: docs/payments-clearing-and-settlement/messages/pacs.004.001.11/README.md -[pacs.007.001.11]: docs/payments-clearing-and-settlement/messages/pacs.007.001.11/README.md -[pacs.008.001.10]: docs/payments-clearing-and-settlement/messages/pacs.008.001.10/README.md -[pacs.009.001.10]: docs/payments-clearing-and-settlement/messages/pacs.009.001.10/README.md -[pacs.010.001.05]: docs/payments-clearing-and-settlement/messages/pacs.010.001.05/README.md -[pacs.028.001.05]: docs/payments-clearing-and-settlement/messages/pacs.028.001.05/README.md -[pain.001.001.03]: docs/payments-initiation/messages/pain.001.001.03/README.md -[pain.001.001.04]: docs/payments-initiation/messages/pain.001.001.04/README.md -[pain.001.001.05]: docs/payments-initiation/messages/pain.001.001.05/README.md -[pain.001.001.06]: docs/payments-initiation/messages/pain.001.001.06/README.md -[pain.001.001.07]: docs/payments-initiation/messages/pain.001.001.07/README.md -[pain.001.001.08]: docs/payments-initiation/messages/pain.001.001.08/README.md -[pain.001.001.09]: docs/payments-initiation/messages/pain.001.001.09/README.md -[pain.001.001.10]: docs/payments-initiation/messages/pain.001.001.10/README.md -[pain.001.001.11]: docs/payments-initiation/messages/pain.001.001.11/README.md - -[banner]: https://kura.pro/pain001/images/banners/banner-pain001.svg 'Pain001' +[pain.001.001.03]: https://www.pain001.com/pain.001.001.03/index.html +[pain.001.001.09]: https://www.pain001.com/pain.001.001.09/index.html + +[banner]: https://kura.pro/pain001/images/banners/banner-pain001.svg 'Pain001, A Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Or SQlite Data Files.' [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/pain001?style=for-the-badge&token=AaUxKfRiou 'Codecov badge' [license-badge]: https://img.shields.io/pypi/l/pain001?style=for-the-badge 'License badge' [pypi-badge]: https://img.shields.io/pypi/pyversions/pain001.svg?style=for-the-badge 'PyPI badge' diff --git a/pain001/__main__.py b/pain001/__main__.py index b2ab750..d0b313c 100644 --- a/pain001/__main__.py +++ b/pain001/__main__.py @@ -17,7 +17,7 @@ """ Enables use of Python Pain001 as a "main" function (i.e. "python3 -m pain001 - + "). This allows using Pain001 with third-party libraries without modifying @@ -82,7 +82,7 @@ ) @click.option( "-s", - "--xsd_template_file_path", + "--xsd_schema_file_path", default=None, type=click.Path(), help="Path to XSD template file (required)", @@ -97,7 +97,7 @@ def main( xml_message_type, xml_template_file_path, - xsd_template_file_path, + xsd_schema_file_path, data_file_path, ): """Initialize the context and log a message.""" @@ -113,8 +113,8 @@ def check_variable(variable, name): # Check that xml_message_type is provided check_variable(xml_message_type, "xml_message_type") - # Check that xsd_template_file_path is provided - check_variable(xsd_template_file_path, "xsd_template_file_path") + # Check that xsd_schema_file_path is provided + check_variable(xsd_schema_file_path, "xsd_schema_file_path") # Check that data_file_path is provided check_variable(data_file_path, "data_file_path") @@ -126,10 +126,10 @@ def check_variable(variable, name): ) sys.exit(1) - # Check that xsd_template_file_path is not invalid - if not os.path.isfile(xsd_template_file_path): + # Check that xsd_schema_file_path is not invalid + if not os.path.isfile(xsd_schema_file_path): print( - f"The XSD template file '{xsd_template_file_path}' does not exist." + f"The XSD template file '{xsd_schema_file_path}' does not exist." ) sys.exit(1) @@ -141,7 +141,7 @@ def check_variable(variable, name): # Check that other necessary arguments are provided if ( xml_template_file_path is None - or xsd_template_file_path is None + or xsd_schema_file_path is None or data_file_path is None ): print(click.get_current_context().get_help()) @@ -149,7 +149,7 @@ def check_variable(variable, name): """ Entrypoint for pain001 when invoked as a module with python3 -m pain001 - . + . """ logger = Context.get_instance().get_logger() @@ -170,12 +170,12 @@ def check_variable(variable, name): ) sys.exit(1) - if not os.path.isfile(xsd_template_file_path): + if not os.path.isfile(xsd_schema_file_path): logger.info( - f"The XSD template file '{xsd_template_file_path}' does not exist." + f"The XSD template file '{xsd_schema_file_path}' does not exist." ) print( - f"The XSD template file '{xsd_template_file_path}' does not exist." + f"The XSD template file '{xsd_schema_file_path}' does not exist." ) sys.exit(1) @@ -186,7 +186,7 @@ def check_variable(variable, name): # Validate the XML file and raise a SystemExit exception if invalid # is_valid = validate_via_xsd( - # xml_template_file_path, xsd_template_file_path + # xml_template_file_path, xsd_schema_file_path # ) # if not is_valid: # logger.error( @@ -197,7 +197,7 @@ def check_variable(variable, name): process_files( xml_message_type, xml_template_file_path, - xsd_template_file_path, + xsd_schema_file_path, data_file_path, ) diff --git a/pain001/core/core.py b/pain001/core/core.py index 3d31118..13b2b46 100644 --- a/pain001/core/core.py +++ b/pain001/core/core.py @@ -32,7 +32,7 @@ def process_files( xml_message_type, xml_template_file_path, - xsd_template_file_path, + xsd_schema_file_path, data_file_path, ): """ @@ -43,7 +43,7 @@ def process_files( xml_message_type (str): The type of XML message to generate. Valid options are 'pain.001.001.03' and 'pain.001.001.09'. xml_template_file_path (str): The path of the XML template file. - xsd_template_file_path (str): The path of the XSD schema file. + xsd_schema_file_path (str): The path of the XSD schema file. data_file_path (str): The path of the CSV or SQLite file containing the payment data. @@ -79,9 +79,9 @@ def process_files( raise FileNotFoundError(error_message) # Check if the XSD schema file exists - if not os.path.exists(xsd_template_file_path): + if not os.path.exists(xsd_schema_file_path): error_message = ( - f"Error: XSD schema file '{xsd_template_file_path}' " + f"Error: XSD schema file '{xsd_schema_file_path}' " f"does not exist." ) logger.error(error_message) @@ -136,7 +136,7 @@ def process_files( mapping, xml_message_type, xml_template_file_path, - xsd_template_file_path, + xsd_schema_file_path, ) # Confirm the XML file has been created @@ -158,7 +158,7 @@ def process_files( [ "", "", - "", + "", "", ] ) diff --git a/requirements.txt b/requirements.txt index 202f73b..bf2cd4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ click==8.1.3 defusedxml==0.7.1 rich==13.4.2 -xmlschema==2.3.0 \ No newline at end of file +xmlschema==2.3.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 0090214..67207d2 100644 --- a/setup.py +++ b/setup.py @@ -176,7 +176,7 @@ "click==8.1.3", "defusedxml==0.7.1", "rich==13.4.2", - "xmlschema==2.3.0", + "xmlschema==2.3.1", ] TEST_DEPENDENCIES = [ @@ -184,7 +184,7 @@ "defusedxml==0.7.1", "pytest>=7.3.1", "rich==13.4.2", - "xmlschema==2.3.0", + "xmlschema==2.3.1", ] NAME = ("pain001",) diff --git a/tests/test_main.py b/tests/test_main.py index 4c58ca1..7f76ca1 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -18,7 +18,7 @@ def test_main_with_valid_files(self): self.xml_message_type, "--xml_template_file_path", self.xml_file, - "--xsd_template_file_path", + "--xsd_schema_file_path", self.xsd_file, "--data_file_path", self.csv_file, @@ -36,7 +36,7 @@ def test_main_with_missing_xml_message_type(self): [ "--xml_template_file_path", self.xml_file, - "--xsd_template_file_path", + "--xsd_schema_file_path", self.xsd_file, "--data_file_path", self.csv_file, @@ -59,8 +59,7 @@ def test_main_with_missing_xsd_template_file(self): ) assert result.exit_code == 1 assert ( - "Error: xsd_template_file_path is required." - in result.output + "Error: xsd_schema_file_path is required." in result.output ) def test_main_with_missing_data_file(self): @@ -71,7 +70,7 @@ def test_main_with_missing_data_file(self): self.xml_message_type, "--xml_template_file_path", self.xml_file, - "--xsd_template_file_path", + "--xsd_schema_file_path", self.xsd_file, ], ) @@ -86,7 +85,7 @@ def test_main_with_invalid_xml_message_type(self): "invalid", "--xml_template_file_path", self.xml_file, - "--xsd_template_file_path", + "--xsd_schema_file_path", self.xsd_file, "--data_file_path", self.csv_file, @@ -103,7 +102,7 @@ def test_main_with_invalid_xml_template_file(self): self.xml_message_type, "--xml_template_file_path", "invalid", - "--xsd_template_file_path", + "--xsd_schema_file_path", self.xsd_file, "--data_file_path", self.csv_file, @@ -123,7 +122,7 @@ def test_main_with_invalid_xsd_template_file(self): self.xml_message_type, "--xml_template_file_path", self.xml_file, - "--xsd_template_file_path", + "--xsd_schema_file_path", "invalid", "--data_file_path", self.csv_file, @@ -143,7 +142,7 @@ def test_main_with_invalid_data_file(self): self.xml_message_type, "--xml_template_file_path", self.xml_file, - "--xsd_template_file_path", + "--xsd_schema_file_path", self.xsd_file, "--data_file_path", "invalid", From 21fb92e64613a82fa8d1c7c9b56224c13ca6fbfd Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Thu, 15 Jun 2023 09:52:24 +0100 Subject: [PATCH 07/11] doc(pain001): content updates --- README.md | 117 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5c33ff0..a4407d4 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,13 @@ Data Files. The Python library focuses specifically on -**Payment Initiation and Advice Messages**, commonly known as **Pain**. It is -currently designed to be compatible with the **pain.001.001.03** and -**pain.001.001.09** message types and will support more message types in the -future. +**Payment Initiation and Advice Messages**, commonly known as **Pain**. In a +very simplified way, a **pain.001** is a message that initiates the customer +payment. + +As of today the library is designed to be compatible with the +**pain.001.001.03** and **pain.001.001.09** message types and will support more +message types in the future. Payments usually start with a **pain.001 payment initiation message**. The payer sends it to the payee (or the payee’s bank) via a secure network. This @@ -33,6 +36,30 @@ the risk of errors, making sure accurate and seamless payment processing. Use the **Pain001** library to simplify, accelerate and automate your payment processing. +## Table of Contents + +- [Pain001: Automate ISO 20022-Compliant Payment File Creation](#pain001-automate-iso-20022-compliant-payment-file-creation) + - [Overview](#overview) + - [Table of Contents](#table-of-contents) + - [Requirements](#requirements) + - [Installation](#installation) + - [Quick Start](#quick-start) + - [Arguments](#arguments) + - [Examples](#examples) + - [Using a CSV Data File as the source](#using-a-csv-data-file-as-the-source) + - [Using a SQLite Data File as the source](#using-a-sqlite-data-file-as-the-source) + - [Using the Source code](#using-the-source-code) + - [Embedded in an Application](#embedded-in-an-application) + - [Validation](#validation) + - [Documentation](#documentation) + - [Supported messages](#supported-messages) + - [Bank-to-Customer Cash Management](#bank-to-customer-cash-management) + - [Payments Clearing and Settlement](#payments-clearing-and-settlement) + - [Payments Initiation](#payments-initiation) + - [License](#license) + - [Contribution](#contribution) + - [Acknowledgements](#acknowledgements) + ## Requirements **Pain001** works with macOS, Linux and Windows and requires Python 3.9.0 and @@ -41,7 +68,7 @@ above. ## Installation It takes just a few seconds to get up and running with **Pain001**. You can -install Pain001 from PyPI with pip or your favorite package manager: +install Pain001 from PyPI with pip or your favourite package manager: Open your terminal and run the following command: @@ -55,7 +82,7 @@ installed. ## Quick Start After installation, you can run **Pain001** directly from the command line. -Simply call the main module pain001 with the paths of your +Simply call the main module pain001 with the paths of your: - **XML template file** containing the various parameters you want to pass from your Data file, @@ -75,20 +102,20 @@ python3 -m pain001 \ ### Arguments -When running **Pain001**, as briefly described above, you will need to specify -four arguments to the **Pain001** module: +When running **Pain001**, you will need to specify four arguments: -- `xml_message_type`: This is the type of XML message you want to generate. +- An `xml_message_type`: This is the type of XML message you want to generate. The currently supported types are: - pain.001.001.03 - pain.001.001.09 -- `xml_template_file_path`: This is the path to the XML template file you are - using that contains variables to be replaced with data from the Data file. -- `xsd_schema_file_path`: This is the path to the XSD schema file you are using - to validate the generated XML file. -- `data_file_path`: This is the path to the CSV or SQLite Data file you want to - convert to XML format. +- An `xml_template_file_path`: This is the path to the XML template file you + are using that contains variables that will be replaced by the values in your + Data file. +- An `xsd_schema_file_path`: This is the path to the XSD schema file you are + using to validate the generated XML file. +- A `data_file_path`: This is the path to the CSV or SQLite Data file you want + to convert to XML format. ## Examples @@ -115,9 +142,9 @@ python3 -m pain001 \ /path/to/your/template.db ``` -### Using the source code +### Using the Source code -You can use the source code and run the example code in your +You can clone the source code and run the example code in your terminal/command-line. To check out the source code, clone the repository from GitHub: @@ -125,25 +152,26 @@ GitHub: git clone https://github.com/sebastienrousseau/pain001.git ``` -You can then run this script to generate a payment initiation message from the -sample CSV Data file: + Then, navigate to the `pain001` directory and run the following command: -```sh -python3 pain001/__main__.py \ - --xml_message_type "pain.001.001.03" \ - --xml_template_file_path "templates/pain.001.001.03/template.xml" \ - --xsd_schema_file_path "templates/pain.001.001.03/pain.001.001.03.xsd" \ - --data_file_path "templates/pain.001.001.03/template.csv" -``` + ```sh + python3 -m pain001 \ + pain.001.001.03 \ + templates/pain.001.001.03/template.xml \ + templates/pain.001.001.03/pain.001.001.03.xsd \ + templates/pain.001.001.03/template.csv + ``` + +This will generate a payment initiation message from the sample CSV Data file. -or the sample SQLite Data File +You can do the same with the sample SQLite Data file: ```sh -python3 pain001/__main__.py \ - --xml_message_type "pain.001.001.03" \ - --xml_template_file_path "templates/pain.001.001.03/template.xml" \ - --xsd_schema_file_path "templates/pain.001.001.03/pain.001.001.03.xsd" \ - --data_file_path "templates/pain.001.001.03/template.db" +python3 -m pain001 \ + pain.001.001.03 \ + templates/pain.001.001.03/template.xml \ + templates/pain.001.001.03/pain.001.001.03.xsd \ + templates/pain.001.001.03/template.db ``` > **Note:** The XML file that **Pain001** generates will automatically be @@ -166,7 +194,12 @@ if __name__ == '__main__': xml_template_file_path = 'template.xml' xsd_schema_file_path = 'schema.xsd' data_file_path = 'data.csv' - main(xml_message_type, xml_template_file_path, xsd_schema_file_path, data_file_path) + main( + xml_message_type, + xml_template_file_path, + xsd_schema_file_path, + data_file_path + ) ``` ### Validation @@ -189,7 +222,7 @@ is_valid = validate_xml_against_xsd( print(f"XML validation result: {is_valid}") ``` -## Documentation 📖 +## Documentation > **Info:** Do check out our [website][0] for comprehensive documentation. @@ -207,9 +240,9 @@ customer. | Status | Message type | Name | |---|---|---| | ⏳ | camt.052.001.10 | Bank-to-Customer Account Statement | -| ⏳ | camt.060.001.10 | Customer Account Notification | -| ⏳ | camt.054.001.10 | Customer Account Statement Request | | ⏳ | camt.053.001.10 | Customer Account Identification | +| ⏳ | camt.054.001.10 | Customer Account Statement Request | +| ⏳ | camt.060.001.10 | Customer Account Notification | #### Payments Clearing and Settlement @@ -245,7 +278,7 @@ and monitor payments. | ⏳ | pain.001.001.10 | Customer Account Closure Request | | ⏳ | pain.001.001.11 | Customer Account Change Request | -## License 📝 +## License The project is licensed under the terms of both the MIT license and the Apache License (Version 2.0). @@ -253,7 +286,7 @@ Apache License (Version 2.0). - [Apache License, Version 2.0][1] - [MIT license][2] -## Contribution 🤝 +## Contribution We welcome contributions to **Pain001**. Please see the [contributing instructions][4] for more information. @@ -263,12 +296,12 @@ submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -## Acknowledgements 💙 +## Acknowledgements We would like to extend a big thank you to all the awesome contributors of [Pain001][5] for their help and support. -[0]: https://Pain001.co +[0]: https://pain001.com [1]: https://opensource.org/license/apache-2-0/ [2]: http://opensource.org/licenses/MIT [3]: https://github.com/sebastienrousseau/pain001 @@ -276,8 +309,8 @@ of [Pain001][5] for their help and support. [5]: https://github.com/sebastienrousseau/pain001/graphs/contributors [6]: https://codecov.io/github/sebastienrousseau/pain001?branch=main -[pain.001.001.03]: https://www.pain001.com/pain.001.001.03/index.html -[pain.001.001.09]: https://www.pain001.com/pain.001.001.09/index.html +[pain.001.001.03]: https://pain001.com/pain.001.001.03/index.html +[pain.001.001.09]: https://pain001.com/pain.001.001.09/index.html [banner]: https://kura.pro/pain001/images/banners/banner-pain001.svg 'Pain001, A Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Or SQlite Data Files.' [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/pain001?style=for-the-badge&token=AaUxKfRiou 'Codecov badge' From 3b87498c41714825860b4172aa39a5169ced48cd Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Thu, 15 Jun 2023 13:47:35 +0100 Subject: [PATCH 08/11] fix(pain001): optimising setup --- README.md | 46 +++++++++++++- setup.cfg | 42 +++++++++---- setup.py | 179 +++++------------------------------------------------- 3 files changed, 88 insertions(+), 179 deletions(-) diff --git a/README.md b/README.md index a4407d4..2cba4ac 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ ![Pain001 banner][banner] -[![PyPI][pypi-badge]][3] [![License][license-badge]][1] -[![Codecov][codecov-badge]][6] +[![PyPI][pypi-badge]][3] [![PyPI Downloads][pypi-downloads-badge]][7] [![License][license-badge]][1] [![Codecov][codecov-badge]][6] ## Overview @@ -11,6 +10,12 @@ **ISO 20022-Compliant Payment Files** directly from your **CSV** or **SQLite** Data Files. +- **Website:** +- **Documentation:** +- **Source code:** +- **Contributing:** +- **Bug reports:** + The Python library focuses specifically on **Payment Initiation and Advice Messages**, commonly known as **Pain**. In a very simplified way, a **pain.001** is a message that initiates the customer @@ -41,6 +46,7 @@ processing. - [Pain001: Automate ISO 20022-Compliant Payment File Creation](#pain001-automate-iso-20022-compliant-payment-file-creation) - [Overview](#overview) - [Table of Contents](#table-of-contents) + - [Features](#features) - [Requirements](#requirements) - [Installation](#installation) - [Quick Start](#quick-start) @@ -60,6 +66,40 @@ processing. - [Contribution](#contribution) - [Acknowledgements](#acknowledgements) +## Features + +- **Easy to use:** Both developers and non-developers can easily use the + library, as it requires minimal coding knowledge. +- **Open-source**: The library is open-source and free to use, making it + accessible to everyone. +- **Secure**: The library is secure and does not store any sensitive data, + making sure that all information remains confidential. +- **Customizable**: The library allows developers to customise the output, + making it adaptable to specific business requirements and preferences. +- **Scalable solution**: The **Pain001** library can handle varying volumes of + payment files, making it suitable for businesses of different sizes and + transaction volumes. +- **Time-saving**: The automated file creation process reduces the time spent + on manual data entry and file generation, increasing overall productivity. +- **Seamless integration**: As a Python package, the Pain001 library is + compatible with various Python-based applications and easily integrates into + any existing projects or workflows. +- **Cross-border compatibility**: The library supports both Single Euro + Payments Area (SEPA) and non-SEPA credit transfers, making it versatile for + use in different countries and regions. +- **Improve accuracy** by providing precise data; the library reduces errors in + payment file creation and processing. +- **Enhance efficiency** by automating the creation of Payment Initiation + message files +- **Accelerate payment file creation** by automating the process and reducing + the time required to create payment files. +- **Guarantee the highest quality and compliance** by validating all payment + files to meet the ISO 20022 standards. +- **Simplify ISO 20022-compliant payment initiation message creation** by + providing a standardised payment file format. +- **Reduce costs** by removing manual data entry and file generation, reducing + payment processing time, and reducing errors. + ## Requirements **Pain001** works with macOS, Linux and Windows and requires Python 3.9.0 and @@ -308,6 +348,7 @@ of [Pain001][5] for their help and support. [4]: https://github.com/sebastienrousseau/pain001/blob/main/CONTRIBUTING.md [5]: https://github.com/sebastienrousseau/pain001/graphs/contributors [6]: https://codecov.io/github/sebastienrousseau/pain001?branch=main +[7]: https://pypi.org/project/pain001/ [pain.001.001.03]: https://pain001.com/pain.001.001.03/index.html [pain.001.001.09]: https://pain001.com/pain.001.001.09/index.html @@ -316,3 +357,4 @@ of [Pain001][5] for their help and support. [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/pain001?style=for-the-badge&token=AaUxKfRiou 'Codecov badge' [license-badge]: https://img.shields.io/pypi/l/pain001?style=for-the-badge 'License badge' [pypi-badge]: https://img.shields.io/pypi/pyversions/pain001.svg?style=for-the-badge 'PyPI badge' +[pypi-downloads-badge]:https://img.shields.io/pypi/dm/pain001.svg?style=for-the-badge 'PyPI Downloads badge' diff --git a/setup.cfg b/setup.cfg index 2f43c6c..aa9de1d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,34 +1,52 @@ +# Copyright (C) 2023 Sebastien Rousseau. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# +# See the License for the specific language governing permissions and +# limitations under the License. + [metadata] name = pain001 version = 0.0.20 +description = Pain001, A Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Or SQlite Data Files. +keywords = pain001,iso20022,payment-processing,automate-payments,sepa,financial,banking-payments,csv,sqlite author = Sebastian Rousseau author_email = sebastian.rousseau@gmail.com -description = pain001 is a Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data. -long_description = file: README.md -long_description_content_type = text/markdown url = https://github.com/sebastienrousseau/pain001 license = Apache Software License license_file = LICENSE-APACHE +long_description = file: README.md +long_description_content_type = text/markdown + classifiers = Development Status :: 4 - Beta Intended Audience :: Developers Intended Audience :: Financial and Insurance Industry - Topic :: Software Development :: Libraries :: Python Modules License :: OSI Approved :: Apache Software License + Operating System :: MacOS + Operating System :: OS Independent + Operating System :: POSIX + Operating System :: Unix Programming Language :: Python Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 - Operating System :: OS Independent - Operating System :: POSIX - Operating System :: MacOS - Operating System :: Unix + Topic :: Software Development :: Libraries :: Python Modules + project_urls = Documentation = https://pain001.com/documentation/ Funding = https://paypal.me/wwdseb Release notes = https://pain001.com/release-notes/ Source = https://github.com/sebastienrousseau/pain001/releases -keywords = iso 20022 pain.001 credit transfer financial banking payments csv sepa [options] packages = @@ -40,11 +58,11 @@ install_requires = rich xmlschema -[wheel] -universal = 1 - [aliases] test = pytest [tool:pytest] testpaths = tests + +[wheel] +universal = 1 diff --git a/setup.py b/setup.py index 67207d2..fb25d25 100644 --- a/setup.py +++ b/setup.py @@ -18,159 +18,13 @@ from setuptools import setup -LONG_DESCRIPTION = """ -**Pain001** is a Python Library for Automating ISO 20022-Compliant Payment -Files Using CSV Data. - -**Pain001** offers a streamlined solution for reducing complexity and costs -associated with payment processing. By providing a simple and efficient method -to create ISO 20022-compliant payment files, it eliminates the manual effort of -file creation and validation. This not only saves valuable time and resources -but also minimizes the risk of errors, ensuring accurate and seamless payment -processing. - -If you are seeking to simplify and automate your payment processing, consider -leveraging the capabilities of **Pain001**. - -## Features ✨ - -- **Easy to use:** The library is easy to use and requires minimal coding -knowledge, making it suitable for both developers and non-developers. -- **Open-source**: The library is open-source and free to use, making it -accessible to everyone. -- **Secure**: The library is secure and does not store any sensitive data, -ensuring that all information remains confidential. -- **Customizable**: The library allows developers to customize the output, -making it adaptable to specific business requirements and preferences. -- **Scalable solution**: The **Pain001** library can handle varying volumes of -payment files, making it suitable for businesses of different sizes and -transaction volumes. -- **Time-saving**: The automated file creation process reduces the time spent -on manual data entry and file generation, increasing overall productivity. -- **Seamless integration**: As a Python package, the **Pain001** library is -compatible with various Python-based applications and easily integrates into -any existing projects or workflows. -- **Cross-border compatibility**: The library supports both Single Euro -Payments Area (SEPA) and non-SEPA credit transfers, making it versatile for -use in different countries and regions. -- **Improve accuracy** by providing precise data, the library reduces errors in -payment file creation and processing. -- **Enhance efficiency** by automating the creation of Payment Initiation -message files -- **Accelerate payment file creation** by automating the process and reducing -the time required to create payment files. -- **Guarantee the highest quality and compliance** by validating all payment -files to meet the ISO 20022 standards. -- **Provide flexibility and choice to migrate to any supported ISO 20022 -messaging standard definitions** by simplifying the message creation process -and providing a standardized format for payment files. - -## Installation - -It takes just a few seconds to get up and running with **Pain001**. Open your -terminal and run the following command: - -```sh -pip install pain001 -``` - -## Usage - -After installation, you can run **Pain001** directly from the command line. -Simply call the main function with the path of your XML template file, XSD -schema file and the path of your CSV file containing the payment data. - -Once you have installed **Pain001**, you can generate and validate XML files -using the following command: - -```sh -python3 -m pain001 \ - \ - \ - \ - -``` - -## Arguments - -When running **Pain001**, you will need to specify four arguments: - -- `xml_message_type`: This is the type of XML message you want to generate. -Currently, the valid options are: - - pain.001.001.03 - - pain.001.001.09 -- `xml_file_path`: This is the path to the XML template file you are using. -- `xsd_file_path`: This is the path to the XSD template file you are using. -- `csv_file_path`: This is the path to the CSV data file you want to convert to -XML. - -## Examples - -Here are a few example on how to use **Pain001** to generate a -pain.001.001.03 XML file from a CSV data file: - -### Via the Command Line - -```sh -python3 -m pain001 \ - pain.001.001.03 \ - /path/to/your/pain.001.001.03.xml \ - /path/to/your/pain.001.001.03.xsd \ - /path/to/your/pain.001.001.03.csv -``` - -**Note:** The XML file that **Pain001** generates will be automatically -validated against the XSD template file before the new XML file is saved. If -the validation fails, **Pain001** will stop running and display an error -message in your terminal. - -### Embedded in an Application - -To embed **Pain001** in a new or existing application, import the main function -and use it in your code. - -Here's an example: - -```python -from pain001 import main - -if __name__ == '__main__': - xml_message_type = 'pain.001.001.03' - xml_file_path = 'template.xml' - xsd_file_path = 'schema.xsd' - csv_file_path = 'data.csv' - main(xml_message_type, xml_file_path, xsd_file_path, csv_file_path) -``` - -### Validation - -To validate the generated XML file against a given xsd schema, use the -following method: - -```python -from pain001.core import validate_xml_against_xsd - -xml_message_type = 'pain.001.001.03' -xml_file = 'generated.xml' -xsd_file = 'schema.xsd' - -is_valid = validate_xml_against_xsd( - xml_message_type, - xml_file, - xsd_file -) -print(f"XML validation result: {is_valid}") -``` - -## Documentation 📖 - -> ℹ️ **Info:** Do check out our for more information on -the Pain001 documentation. -""".strip() +with open("README.md") as f: + LONG_DESCRIPTION = f.read() SHORT_DESCRIPTION = """ -Pain001 is a Python Library for Automating ISO 20022-Compliant Payment Files -Using CSV Data.""".strip() +Pain001, A Python Library for Automating ISO 20022-Compliant Payment Files +Using CSV Or SQlite Data Files. +""".strip() DEPENDENCIES = [ "click==8.1.3", @@ -180,14 +34,10 @@ ] TEST_DEPENDENCIES = [ - "click==8.1.3", - "defusedxml==0.7.1", "pytest>=7.3.1", - "rich==13.4.2", - "xmlschema==2.3.1", ] -NAME = ("pain001",) +NAME = "pain001" URL = "https://github.com/sebastienrousseau/pain001" VERSION = "0.0.20" @@ -196,6 +46,7 @@ version=VERSION, description=SHORT_DESCRIPTION, long_description=LONG_DESCRIPTION, + long_description_content_type="text/markdown", url=URL, author="Sebastien Rousseau", author_email="sebastian.rousseau@gmail.com", @@ -204,21 +55,19 @@ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Intended Audience :: Financial and Insurance Industry", - "Topic :: Software Development :: Libraries :: Python Modules", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Operating System :: MacOS", "Operating System :: OS Independent", "Operating System :: POSIX", - "Operating System :: MacOS", "Operating System :: Unix", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python", + "Topic :: Software Development :: Libraries :: Python Modules", ], keywords=""" - Pain001, finance python library, ISO 20022, payment files, payment - processing, automate payments, ISO 20022-compliant, SWIFT, SEPA, payment - initiation messages, + pain001,iso20022,payment-processing,automate-payments,sepa,financial,banking-payments,csv,sqlite """, packages=["pain001"], install_requires=DEPENDENCIES, From 5edc5794f24170634b22ad512e159b7400e7c25d Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Thu, 15 Jun 2023 13:59:16 +0100 Subject: [PATCH 09/11] doc(pain001): updated template --- TEMPLATE.md | 85 +++++++++++++---------------------------------------- 1 file changed, 20 insertions(+), 65 deletions(-) diff --git a/TEMPLATE.md b/TEMPLATE.md index 801dc3b..8dbea60 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -1,81 +1,36 @@ - - -pain001 logo - - - -# Pain001 (v0.0.20) +# Pain001: Automate ISO 20022-Compliant Payment File Creation ![Pain001 banner][banner] -[![PyPI][pypi]][2] [![License][license]][1] [![Codecov][codecov]][3] +## Overview -**Pain001** is a Python Library for Automating ISO 20022-Compliant Payment -Files Using CSV Data. +**Pain001** is an open-source Python Library that you can use to create +**ISO 20022-Compliant Payment Files** directly from your **CSV** or **SQLite** +Data Files. -**Pain001** offers a streamlined solution for reducing complexity and costs -associated with payment processing. By providing a simple and efficient method -to create ISO 20022-compliant payment files, it eliminates the manual effort of -file creation and validation. This not only saves valuable time and resources -but also minimizes the risk of errors, ensuring accurate and seamless payment -processing. +- **Website:** +- **Documentation:** +- **Source code:** +- **Contributing:** +- **Bug reports:** -If you are seeking to simplify and automate your payment processing, consider -leveraging the capabilities of **Pain001**. +## Requirements -## Features ✨ - -- **Easy to use:** The library is easy to use and requires minimal coding - knowledge, making it suitable for both developers and non-developers. -- **Open-source**: The library is open-source and free to use, making it - accessible to everyone. -- **Secure**: The library is secure and does not store any sensitive data, - ensuring that all information remains confidential. -- **Customizable**: The library allows developers to customize the output, - making it adaptable to specific business requirements and preferences. -- **Scalable solution**: The **Pain001** library can handle varying volumes of - payment files, making it suitable for businesses of different sizes and - transaction volumes. -- **Time-saving**: The automated file creation process reduces the time spent - on manual data entry and file generation, increasing overall productivity. -- **Seamless integration**: As a Python package, the **Pain001** library is - compatible with various Python-based applications and easily integrates into - any existing projects or workflows. -- **Cross-border compatibility**: The library supports both Single Euro - Payments Area (SEPA) and non-SEPA credit transfers, making it versatile for - use in different countries and regions. -- **Improve accuracy** by providing precise data, the library reduces errors in - payment file creation and processing. -- **Enhance efficiency** by automating the creation of Payment Initiation - message files -- **Accelerate payment file creation** by automating the process and reducing - the time required to create payment files. -- **Guarantee the highest quality and compliance** by validating all payment - files to meet the ISO 20022 standards. -- **Provide flexibility and choice to migrate to any supported ISO 20022 - messaging standard definitions** by simplifying the message creation process - and providing a standardized format for payment files. +**Pain001** works with macOS, Linux and Windows and requires Python 3.9.0 and +above. ## Installation -It takes just a few seconds to get up and running with **Pain001**. Open your -terminal and run the following command: +It takes just a few seconds to get up and running with **Pain001**. You can +install Pain001 from PyPI with pip or your favourite package manager: + +Open your terminal and run the following command: ```sh pip install pain001 ``` -[1]: https://opensource.org/license/apache-2-0/ -[2]: https://github.com/sebastienrousseau/pain001 -[3]: https://codecov.io/github/sebastienrousseau/pain001?branch=main +Add the -U switch to update to the current version, if `pain001` is already +installed. -[banner]: https://kura.pro/pain001/images/titles/title-pain001.svg 'Pain001 banner' -[codecov]: https://img.shields.io/codecov/c/github/sebastienrousseau/pain001?style=for-the-badge&token=AaUxKfRiou 'Codecov badge' -[license]: https://img.shields.io/pypi/l/pain001?style=for-the-badge 'License badge' -[pypi]: https://img.shields.io/pypi/pyversions/pain001.svg?style=for-the-badge 'PyPI badge' +[banner]: https://kura.pro/pain001/images/banners/banner-pain001.svg 'Pain001, A Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Or SQlite Data Files.' From 981a668373a7243a4b79194f01f4fdc980b7a346 Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Fri, 16 Jun 2023 09:32:07 +0100 Subject: [PATCH 10/11] fix(pain001): poetry updates --- README.md | 2 - TEMPLATE.md | 2 - poetry.lock | 133 +++++++++++++++++++++++++++++++++++++++++++------ pyproject.toml | 3 ++ setup.cfg | 14 ++++-- 5 files changed, 129 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 2cba4ac..209de8d 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,7 @@ Data Files. - **Website:** -- **Documentation:** - **Source code:** -- **Contributing:** - **Bug reports:** The Python library focuses specifically on diff --git a/TEMPLATE.md b/TEMPLATE.md index 8dbea60..71a66d5 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -9,9 +9,7 @@ Data Files. - **Website:** -- **Documentation:** - **Source code:** -- **Contributing:** - **Bug reports:** ## Requirements diff --git a/poetry.lock b/poetry.lock index f8facae..9a35864 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,41 +1,142 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] [[package]] name = "elementpath" -version = "2.5.3" -description = "XPath 1.0/2.0/3.0 parsers and selectors for ElementTree and lxml" -category = "main" +version = "4.1.2" +description = "XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml" +optional = false +python-versions = ">=3.7" +files = [ + {file = "elementpath-4.1.2-py3-none-any.whl", hash = "sha256:e8a6c5685e1843c620f426c85ad21ff25cfc7554790116b1046adae7dc252458"}, + {file = "elementpath-4.1.2.tar.gz", hash = "sha256:0bd0ef5bad559b677ba499e9c7342ca1f2ae2bace90808ee52528ec8d9f6e12b"}, +] + +[package.extras] +dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", "memray", "mypy", "tox", "xmlschema (>=2.0.0)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" optional = false python-versions = ">=3.7" files = [ - {file = "elementpath-2.5.3-py3-none-any.whl", hash = "sha256:5ef1d51e8daa670f007914ff0f78ca7b2ecaa47e0ea0c5c699a29e6bc5f50385"}, - {file = "elementpath-2.5.3.tar.gz", hash = "sha256:b8aeb6f27dddc10fb9201b62090628a846cbae8577f3544cb1075fa38d0817f6"}, + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "rich" +version = "13.4.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.4.2-py3-none-any.whl", hash = "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec"}, + {file = "rich-13.4.2.tar.gz", hash = "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + [package.extras] -dev = ["Sphinx", "coverage", "flake8", "lxml", "memory-profiler", "mypy (==0.950)", "tox", "xmlschema (>=1.9.0)"] +jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "xmlschema" -version = "1.11.3" +version = "2.3.1" description = "An XML Schema validator and decoder" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "xmlschema-1.11.3-py3-none-any.whl", hash = "sha256:56cb8c028457fa8948587339a1c3fa75a0317472ab6710f34ba2c0cb57d6b710"}, - {file = "xmlschema-1.11.3.tar.gz", hash = "sha256:28a135028f7ab1e0c934fc0c6717a66b2dc5f166d123dfe6ce61afc671ad113f"}, + {file = "xmlschema-2.3.1-py3-none-any.whl", hash = "sha256:eac0e10957723689ff0691785da4ffee1e95df3a874e685a179047f7bf07f8fb"}, + {file = "xmlschema-2.3.1.tar.gz", hash = "sha256:2eb426c5710833a05610c22c8766713a1b87e9405e3eca0b7c658375bf7ec810"}, ] [package.dependencies] -elementpath = ">=2.5.0,<3.0.0" +elementpath = ">=4.1.2,<5.0.0" [package.extras] -codegen = ["elementpath (>=2.5.0,<3.0.0)", "jinja2"] -dev = ["Sphinx", "coverage", "elementpath (>=2.5.0,<3.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] -docs = ["Sphinx", "elementpath (>=2.5.0,<3.0.0)", "jinja2", "sphinx-rtd-theme"] +codegen = ["elementpath (>=4.1.2,<5.0.0)", "jinja2"] +dev = ["Sphinx", "coverage", "elementpath (>=4.1.2,<5.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] +docs = ["Sphinx", "elementpath (>=4.1.2,<5.0.0)", "jinja2", "sphinx-rtd-theme"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "5b9996124b720ddc00471a99a0cedec3a3c98d88b0eb31ec3e6ed56b3b28ab07" +content-hash = "a658f3aa8267479aea31fe66e6c80d643ffea5d40ada3d4be3e1b13e97f4b6bf" diff --git a/pyproject.toml b/pyproject.toml index 78ec1e8..fa1ab4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,9 @@ homepage = "https://pain001.com" [tool.poetry.dependencies] python = "^3.9" xmlschema = "^2.3.0" +click = "^8.1.3" +defusedxml = "^0.7.1" +rich = "^13.4.2" [build-system] requires = ["poetry-core"] diff --git a/setup.cfg b/setup.cfg index aa9de1d..fe30a67 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,8 +22,10 @@ keywords = pain001,iso20022,payment-processing,automate-payments,sepa,financial, author = Sebastian Rousseau author_email = sebastian.rousseau@gmail.com url = https://github.com/sebastienrousseau/pain001 -license = Apache Software License -license_file = LICENSE-APACHE +license = Apache License 2.0 +license_files = + LICENSE-APACHE + LICENSE-MIT long_description = file: README.md long_description_content_type = text/markdown @@ -43,15 +45,16 @@ classifiers = Topic :: Software Development :: Libraries :: Python Modules project_urls = - Documentation = https://pain001.com/documentation/ + Documentation = https://pain001.com/ Funding = https://paypal.me/wwdseb - Release notes = https://pain001.com/release-notes/ Source = https://github.com/sebastienrousseau/pain001/releases [options] packages = pain001 - +zip_safe = False +include_package_data = True +python_requires = ~=3.9 install_requires = click defusedxml @@ -66,3 +69,4 @@ testpaths = tests [wheel] universal = 1 + From ce9dd7f6846a163e390e289a777890d64d2fec7c Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Fri, 16 Jun 2023 09:50:52 +0100 Subject: [PATCH 11/11] fix(pain001): minor tweaks --- .bandit.yml | 4 +- templates/template_updated.xml | 473 --------------------------------- 2 files changed, 2 insertions(+), 475 deletions(-) delete mode 100644 templates/template_updated.xml diff --git a/.bandit.yml b/.bandit.yml index f487e59..e5bd7f3 100644 --- a/.bandit.yml +++ b/.bandit.yml @@ -1,9 +1,9 @@ exclude_dirs: - - .tox - .git + - .tox + - "tests/*" - build - dist - - "tests/*" skips: - B101 \ No newline at end of file diff --git a/templates/template_updated.xml b/templates/template_updated.xml deleted file mode 100644 index 39808ad..0000000 --- a/templates/template_updated.xml +++ /dev/null @@ -1,473 +0,0 @@ - - - - - 1 - 2023-03-10T15:30:47.000Z - 2 - - John Doe - - - - Payment-Info-12345 - TRF - true - 2 - 2 - - - SEPA - - - 2023-03-12 - - Acme Corp - - - - DE75512108001245126162 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID6789 - - - 150.00 - - - - SPUEDE2UXXX - - - - Global Tech - - - Invoice-98765 - - - - - Payment-Info-12345 - TRF - true - 2 - 2 - - - SEPA - - - 2023-03-12 - - Acme Corp - - - - DE75512108001245126162 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID6789 - - - 150.00 - - - - SPUEDE2UXXX - - - - Global Tech - - - Invoice-98765 - - - - - Payment-Info-67890 - TRF - true - 3 - 3 - - - SEPA - - - 2023-03-14 - - Brown Industries - - - - DE44500105175407324931 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID4321 - - - 300.00 - - - - SPUEDE2UXXX - - - - Green Energy - - - Invoice-12345 - - - - - Payment-Info-67890 - TRF - true - 3 - 3 - - - SEPA - - - 2023-03-14 - - Brown Industries - - - - DE44500105175407324931 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID4321 - - - 300.00 - - - - SPUEDE2UXXX - - - - Green Energy - - - Invoice-12345 - - - - - Payment-Info-24680 - TRF - true - 1 - 1 - - - SEPA - - - 2023-03-13 - - Alpha Electronics - - - - DE47500105175711000100 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID1357 - - - 250.00 - - - - SPUEDE2UXXX - - - - Beta Chemicals - - - Invoice-24680 - - - - - Payment-Info-24680 - TRF - true - 1 - 1 - - - SEPA - - - 2023-03-13 - - Alpha Electronics - - - - DE47500105175711000100 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID1357 - - - 250.00 - - - - SPUEDE2UXXX - - - - Beta Chemicals - - - Invoice-24680 - - - - - Payment-Info-86420 - TRF - true - 2 - 2 - - - SEPA - - - 2023-03-15 - - Delta Manufacturing - - - - DE25500105176602214896 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID2468 - - - 175.00 - - - - SPUEDE2UXXX - - - - Gamma Logistics - - - Invoice-86420 - - - - - Payment-Info-86420 - TRF - true - 2 - 2 - - - SEPA - - - 2023-03-15 - - Delta Manufacturing - - - - DE25500105176602214896 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID2468 - - - 175.00 - - - - SPUEDE2UXXX - - - - Gamma Logistics - - - Invoice-86420 - - - - - Payment-Info-59162 - TRF - true - 3 - 3 - - - SEPA - - - 2023-03-16 - - Smart Solutions - - - - DE86500105174603245678 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID4815 - - - 225.00 - - - - SPUEDE2UXXX - - - - Eco Innovations - - - Invoice-59162 - - - - - Payment-Info-59162 - TRF - true - 3 - 3 - - - SEPA - - - 2023-03-16 - - Smart Solutions - - - - DE86500105174603245678 - - - - - BANKDEFFXXX - - - SLEV - - - PaymentID4815 - - - 225.00 - - - - SPUEDE2UXXX - - - - Eco Innovations - - - Invoice-59162 - - - - -