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/README.md b/README.md
index 9843c09..209de8d 100644
--- a/README.md
+++ b/README.md
@@ -1,57 +1,91 @@
-
+# Pain001: Automate ISO 20022-Compliant Payment File Creation
-
+![Pain001 banner][banner]
-
+[![PyPI][pypi-badge]][3] [![PyPI Downloads][pypi-downloads-badge]][7] [![License][license-badge]][1] [![Codecov][codecov-badge]][6]
-# Pain001 - A Python Library for Automating ISO 20022-Compliant Payment Files Using CSV Data
+## Overview
-![Pain001 banner][banner]
+**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.
-[![PyPI][pypi-badge]][3] [![License][license-badge]][1]
-[![Codecov][codecov-badge]][6]
+- **Website:**
+- **Source code:**
+- **Bug reports:**
-**Pain001** is a Python Library for Automating ISO 20022-Compliant Payment
-Files Using CSV Data.
+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
+payment.
-**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.
+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.
-If you are seeking to simplify and automate your payment processing, consider
-leveraging the capabilities of **Pain001**.
+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.
-## Features ✨
+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.
-- **Easy to use:** The library is easy to use and requires minimal coding
- knowledge, making it suitable for both developers and non-developers.
+## 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)
+ - [Features](#features)
+ - [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)
+
+## 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,
- ensuring that all information remains confidential.
-- **Customizable**: The library allows developers to customize the output,
+ 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
+- **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
+- **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
@@ -59,65 +93,126 @@ leveraging the capabilities of **Pain001**.
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.
+- **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
+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
```
-## 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:
-- `xml_message_type`: This is the type of XML message you want to generate.
- Currently, the valid options are:
+- 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_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.
+- 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
-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/template.db
+```
+
+### Using the Source code
+
+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:
+
+```sh
+git clone https://github.com/sebastienrousseau/pain001.git
+```
+
+ Then, navigate to the `pain001` directory and run the following command:
+
+ ```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.
+
+You can do the same with the sample SQLite Data file:
+
+```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.db
```
-**Note:** The XML file that **Pain001** generates will be automatically
+> **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.
@@ -134,10 +229,15 @@ 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
@@ -160,39 +260,9 @@ is_valid = validate_xml_against_xsd(
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:
+## Documentation
-- **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
@@ -207,10 +277,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.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
@@ -219,14 +289,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
@@ -237,16 +307,16 @@ 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 📝
+## License
The project is licensed under the terms of both the MIT license and the
Apache License (Version 2.0).
@@ -254,7 +324,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.
@@ -264,42 +334,25 @@ 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
[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
-[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/titles/title-pain001.svg 'Pain001'
+[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'
+[pypi-downloads-badge]:https://img.shields.io/pypi/dm/pain001.svg?style=for-the-badge 'PyPI Downloads badge'
diff --git a/TEMPLATE.md b/TEMPLATE.md
index 6f10c54..71a66d5 100644
--- a/TEMPLATE.md
+++ b/TEMPLATE.md
@@ -1,81 +1,34 @@
-
-
-
-
-
-
-# Pain001 (v0.0.19)
+# 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:**
+- **Source code:**
+- **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.'
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..d0b313c 100644
--- a/pain001/__main__.py
+++ b/pain001/__main__.py
@@ -17,142 +17,143 @@
"""
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
-
-from pain001.xml.validate_via_xsd import validate_via_xsd
-
-
-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**.
+import click
-## Installation
-
-To install **Pain001**, run this command in your terminal:
-
-```sh
-pip install pain001
-```
-
-## Usage
+from pain001.constants.constants import valid_xml_types
+from pain001.context.context import Context
+from pain001.core.core import process_files
-To use **Pain001**, run this command in your terminal:
+# from pain001.xml.validate_via_xsd import validate_via_xsd
-```sh
-python3 -m pain001 \
- \
- \
- \
-
-```
+from rich.console import Console
+from rich.table import Table
+from rich import box
-## 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_schema_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_schema_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_schema_file_path is provided
+ check_variable(xsd_schema_file_path, "xsd_schema_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_schema_file_path is not invalid
+ if not os.path.isfile(xsd_schema_file_path):
+ print(
+ f"The XSD template file '{xsd_schema_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_schema_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 +161,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_schema_file_path):
logger.info(
- "The XSD template file '{xsd_file_path}' does not exist."
+ f"The XSD template file '{xsd_schema_file_path}' does not exist."
+ )
+ print(
+ f"The XSD template file '{xsd_schema_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 +185,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."
- )
- sys.exit(1)
+ # is_valid = validate_via_xsd(
+ # xml_template_file_path, xsd_schema_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_schema_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..13b2b46 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_schema_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_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.
- 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,17 +70,19 @@ def process_files(
raise ValueError(error_message)
# Check if the XML template file exists
- if not os.path.exists(xml_file_path):
+ if not os.path.exists(xml_template_file_path):
error_message = (
- f"Error: XML template '{xml_file_path}' does not exist."
+ 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_file_path):
+ if not os.path.exists(xsd_schema_file_path):
error_message = (
- f"Error: XSD schema file '{xsd_file_path}' does not exist."
+ f"Error: XSD schema file '{xsd_schema_file_path}' "
+ f"does not exist."
)
logger.error(error_message)
raise FileNotFoundError(error_message)
@@ -112,53 +112,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_schema_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..0ee147e 100644
--- a/pain001/xml/validate_via_xsd.py
+++ b/pain001/xml/validate_via_xsd.py
@@ -15,19 +15,37 @@
# 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
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/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 07cff7b..fa1ab4f 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"
@@ -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/requirements.txt b/requirements.txt
index 45cae35..bf2cd4e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,4 @@
-xmlschema==2.3.0
\ No newline at end of file
+click==8.1.3
+defusedxml==0.7.1
+rich==13.4.2
+xmlschema==2.3.1
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index b496816..fe30a67 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,47 +1,72 @@
+# 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.19
+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.
+url = https://github.com/sebastienrousseau/pain001
+license = Apache License 2.0
+license_files =
+ LICENSE-APACHE
+ LICENSE-MIT
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
+
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/
+ Documentation = https://pain001.com/
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 =
pain001
-
+zip_safe = False
+include_package_data = True
+python_requires = ~=3.9
install_requires =
+ click
+ defusedxml
+ rich
xmlschema
-[wheel]
-universal = 1
-
[aliases]
test = pytest
[tool:pytest]
testpaths = tests
+
+[wheel]
+universal = 1
+
diff --git a/setup.py b/setup.py
index 92027ea..fb25d25 100644
--- a/setup.py
+++ b/setup.py
@@ -18,173 +18,35 @@
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()
-
-DEPENDENCIES = ["xmlschema>=2.3.0"]
+Pain001, A Python Library for Automating ISO 20022-Compliant Payment Files
+Using CSV Or SQlite Data Files.
+""".strip()
-TEST_DEPENDENCIES = ["xmlschema>=2.3.0", "pytest>=7.3.1"]
+DEPENDENCIES = [
+ "click==8.1.3",
+ "defusedxml==0.7.1",
+ "rich==13.4.2",
+ "xmlschema==2.3.1",
+]
-VERSION = "0.0.19"
+TEST_DEPENDENCIES = [
+ "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,
+ long_description_content_type="text/markdown",
url=URL,
author="Sebastien Rousseau",
author_email="sebastian.rousseau@gmail.com",
@@ -193,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,
diff --git a/templates/pain.001.001.03/template.db b/templates/pain.001.001.03/template.db
new file mode 100644
index 0000000..ab37c32
Binary files /dev/null and b/templates/pain.001.001.03/template.db differ
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/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
-
-
-
-
-
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_context.py b/tests/test_context.py
index 9aa1bec..507b1b1 100644
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -5,49 +5,92 @@
class TestContext(unittest.TestCase):
+ """Unit tests for the Context class."""
+
def setUp(self):
- self.context = Context.get_instance()
- self.context.set_name("test_context")
+ """Set up the test fixture."""
+ Context.instance = None
def tearDown(self):
- self.context.logger = None
+ """Tear down the test fixture."""
+ 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
+ """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.
- context = self.context
- context.set_log_level(logging.DEBUG)
- self.assertEqual(context.log_level, logging.DEBUG)
+ """Test that set_log_level() sets the log level of the logger"""
+ context = Context.get_instance()
- # Test that set_log_level() raises an exception if the log level is
- # invalid.
+ """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
+ """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."""
+ 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..3792194 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1,11 +1,10 @@
-import os
import pytest
import sys
from contextlib import contextmanager
from io import StringIO
-from pain001.core import process_files
+from pain001.core.core import process_files
@contextmanager
@@ -18,13 +17,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 +28,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 +42,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 +60,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 +76,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 +89,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 +101,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 +113,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 +132,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 +143,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_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 7db484a..7f76ca1 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,79 +1,154 @@
-import io
-import pytest
-import sys
+from click.testing import CliRunner
from pain001.__main__ import main
-from unittest.mock import patch
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_schema_file_path",
self.xsd_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 (
+ "The XML has been validated against tests/data/template.xsd\n"
+ in result.output
+ )
+ assert result.exit_code == 0
- def test_main_with_invalid_csv_file(self):
- with patch.object(
- sys,
- "argv",
+ def test_main_with_missing_xml_message_type(self):
+ result = self.runner.invoke(
+ main,
[
- "",
+ "--xml_template_file_path",
+ self.xml_file,
+ "--xsd_schema_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,
+ ],
+ )
+ assert result.exit_code == 1
+ assert (
+ "Error: xsd_schema_file_path is required." in result.output
+ )
+
+ 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_schema_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_schema_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_schema_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
+ )
+
+ 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_schema_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
+ )
- assert exc_info.type == SystemExit
- assert exc_info.value.code == 1
+ 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_schema_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
+ )
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",