From 176c5de08465e02099bc1ebbffb2e446c2082942 Mon Sep 17 00:00:00 2001 From: sreevardhanreddi Date: Tue, 28 Feb 2023 18:52:02 +0530 Subject: [PATCH 1/3] feat: added python setup --- .gitignore | 36 +++++++++++++++ pyproject.toml | 26 +++++++++++ setup.py | 31 +++++++++++++ src/py_ifsc/__init__.py | 3 ++ src/py_ifsc/ifsc.py | 97 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+) create mode 100644 pyproject.toml create mode 100644 setup.py create mode 100644 src/py_ifsc/__init__.py create mode 100644 src/py_ifsc/ifsc.py diff --git a/.gitignore b/.gitignore index 6a74b70c..cfc3b9c1 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,39 @@ tmp/ # Temp files .*~ +# python +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + + +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +.vscode + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..c1bf40d3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "py-ifsc" +version = "0.0.1" +authors = [ + { name="sreevardhanreddi", email="sreevardhanreddi@gmail.com" }, +] +description = "This is part of the IFSC toolset released by Razorpay. You can find more details about the entire release at [ifsc.razorpay.com](https://ifsc.razorpay.com)." +readme = "README.md" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] +dependencies = [ + "requests", +] + + +[project.urls] +"Homepage" = "https://github.com/razorpay/ifsc" +"Bug Tracker" = "https://github.com/razorpay/ifsc/issues" + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..7f808f9a --- /dev/null +++ b/setup.py @@ -0,0 +1,31 @@ +import os +import shutil +import glob +import pathlib +from setuptools import setup, find_packages + + +here = pathlib.Path(__file__).parent.resolve() + +long_description = (here / "README.md").read_text(encoding="utf-8") + + +try: + json_files = glob.glob(str(here / "src" / "*.json")) + os.makedirs(str(here / "src" / "py_ifsc" / "data"), exist_ok=True) + for json_file in json_files: + os.symlink( + json_file, + str(here / "src" / "py_ifsc" / "data" / os.path.basename(json_file)), + ) + setup( + name="py_ifsc", + version="0.0.1", + include_package_data=True, + package_data={"py_ifsc.data": ["*.json"]}, + zip_safe=False, + ) +except Exception as e: + print(e) +finally: + shutil.rmtree(str(here / "src" / "py_ifsc" / "data")) diff --git a/src/py_ifsc/__init__.py b/src/py_ifsc/__init__.py new file mode 100644 index 00000000..c83b798c --- /dev/null +++ b/src/py_ifsc/__init__.py @@ -0,0 +1,3 @@ +from .ifsc import IFSC + +__all__ = ["IFSC"] diff --git a/src/py_ifsc/ifsc.py b/src/py_ifsc/ifsc.py new file mode 100644 index 00000000..6a300d99 --- /dev/null +++ b/src/py_ifsc/ifsc.py @@ -0,0 +1,97 @@ +import json +import requests +from importlib.resources import path + + +import pdb + +pdb.set_trace() +with open(path("py_ifsc", "IFSC.json")) as f: + ifscs = json.load(f) + +with open(path("py_ifsc", "banknames.json")) as f: + bank_names = json.load(f) + +with open(path("py_ifsc", "sublet.json")) as f: + sublets = json.load(f) + +with open(path("py_ifsc", "custom-sublets.json")) as f: + custom_sublets = json.load(f) + + +class IFSC: + def __init__(self): + self.base_url = "https://ifsc.razorpay.com/" + self.ifscs = ifscs + self.bank_names = bank_names + self.sublets = sublets + self.custom_sublets = custom_sublets + + def validate(self, code): + if len(code) != 11: + return False + + if code[4] != "0": + return False + + _bank_code = code[:4].upper() + _branch_code = code[5:].upper() + + if not _bank_code in self.ifscs: + return False + + if _branch_code.isnumeric(): + _branch_code = int(_branch_code) + return _branch_code in self.ifscs[_bank_code] + + return _branch_code in self.ifscs[_bank_code] + + def get_details(self, code): + url = self.base_url + code + if not self.validate(code): + raise ValueError("Invalid IFSC code") + + data = {} + try: + res = requests.get(url) + if res.status_code == 200: + data = res.json() + elif res.status_code == 404: + raise ValueError("Invalid IFSC code") + else: + raise ValueError( + "IFSC API returned an invalid response for {}".format(code) + ) + except Exception as e: + raise ValueError("Invalid IFSC code") + + return data + + def get_bank_name(self, code: str) -> str: + if code in self.bank_names: + return self.bank_names[code] + + if self.validate(code): + if code in self.sublets: + bank_code = self.sublets[code] + return self.bank_names[bank_code] + else: + return self.get_custom_sublet(code) + else: + raise ValueError("Invalid IFSC code") + + def get_custom_sublet(self, code: str) -> str: + for key, value in self.custom_sublets: + if len(code) >= len(key) and code[: len(key)] == key: + if value in self.bank_names: + return self.bank_names[value] + else: + return value + + raise ValueError("Invalid IFSC code") + + +if __name__ == "__main__": + ifsc = IFSC() + print(ifsc.get_bank_name("SBIN0000001")) + print(ifsc.get_details("SBIN0000001")) From 4d21dbc2bd269bbca395ba781bbc4a6384a0f712 Mon Sep 17 00:00:00 2001 From: sreevardhanreddi Date: Tue, 28 Feb 2023 21:48:15 +0530 Subject: [PATCH 2/3] feat: changed directory structure --- pyproject.toml | 2 +- setup.py | 17 +++++++++-------- src/{py_ifsc => python}/__init__.py | 0 src/{py_ifsc => python}/ifsc.py | 14 ++++++-------- 4 files changed, 16 insertions(+), 17 deletions(-) rename src/{py_ifsc => python}/__init__.py (100%) rename src/{py_ifsc => python}/ifsc.py (89%) diff --git a/pyproject.toml b/pyproject.toml index c1bf40d3..e4547eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "py-ifsc" +name = "ifsc" version = "0.0.1" authors = [ { name="sreevardhanreddi", email="sreevardhanreddi@gmail.com" }, diff --git a/setup.py b/setup.py index 7f808f9a..fec79291 100644 --- a/setup.py +++ b/setup.py @@ -5,27 +5,28 @@ from setuptools import setup, find_packages -here = pathlib.Path(__file__).parent.resolve() +BASE_DIR = pathlib.Path(__file__).parent.resolve() -long_description = (here / "README.md").read_text(encoding="utf-8") +long_description = (BASE_DIR / "README.md").read_text(encoding="utf-8") try: - json_files = glob.glob(str(here / "src" / "*.json")) - os.makedirs(str(here / "src" / "py_ifsc" / "data"), exist_ok=True) + json_files = glob.glob(str(BASE_DIR / "src" / "*.json")) + os.makedirs(str(BASE_DIR / "src" / "python" / "data"), exist_ok=True) for json_file in json_files: os.symlink( json_file, - str(here / "src" / "py_ifsc" / "data" / os.path.basename(json_file)), + str(BASE_DIR / "src" / "python" / "data" / os.path.basename(json_file)), ) setup( - name="py_ifsc", + name="ifsc", version="0.0.1", include_package_data=True, - package_data={"py_ifsc.data": ["*.json"]}, + package_dir={"ifsc": "src/python"}, + package_data={"ifsc.data": ["*.json"]}, zip_safe=False, ) except Exception as e: print(e) finally: - shutil.rmtree(str(here / "src" / "py_ifsc" / "data")) + shutil.rmtree(str(BASE_DIR / "src" / "python" / "data")) diff --git a/src/py_ifsc/__init__.py b/src/python/__init__.py similarity index 100% rename from src/py_ifsc/__init__.py rename to src/python/__init__.py diff --git a/src/py_ifsc/ifsc.py b/src/python/ifsc.py similarity index 89% rename from src/py_ifsc/ifsc.py rename to src/python/ifsc.py index 6a300d99..c480aeff 100644 --- a/src/py_ifsc/ifsc.py +++ b/src/python/ifsc.py @@ -1,21 +1,19 @@ import json import requests -from importlib.resources import path +from importlib.resources import files +pkg_path = files("ifsc.data") -import pdb - -pdb.set_trace() -with open(path("py_ifsc", "IFSC.json")) as f: +with open(pkg_path.joinpath("IFSC.json")) as f: ifscs = json.load(f) -with open(path("py_ifsc", "banknames.json")) as f: +with open(pkg_path.joinpath("banknames.json")) as f: bank_names = json.load(f) -with open(path("py_ifsc", "sublet.json")) as f: +with open(pkg_path.joinpath("sublet.json")) as f: sublets = json.load(f) -with open(path("py_ifsc", "custom-sublets.json")) as f: +with open(pkg_path.joinpath("custom-sublets.json")) as f: custom_sublets = json.load(f) From 65a921ef2a01324e0cd7879a1ffc28d1ea962ce9 Mon Sep 17 00:00:00 2001 From: sreevardhanreddi Date: Mon, 13 Mar 2023 20:28:30 +0530 Subject: [PATCH 3/3] feat: updated readme md, added bank related code --- README.md | 109 ++++++++++++++++++++++---------- pyproject.toml | 33 +++------- setup.py | 30 +++++++-- src/python/__init__.py | 3 +- src/python/bank.py | 27 ++++++++ src/python/ifsc.py | 30 ++++----- src/python/tests/__init__.py | 0 src/python/tests/test_bank.py | 43 +++++++++++++ src/python/tests/test_client.py | 79 +++++++++++++++++++++++ src/python/tests/test_ifsc.py | 52 +++++++++++++++ 10 files changed, 322 insertions(+), 84 deletions(-) create mode 100644 src/python/bank.py create mode 100644 src/python/tests/__init__.py create mode 100644 src/python/tests/test_bank.py create mode 100644 src/python/tests/test_client.py create mode 100644 src/python/tests/test_ifsc.py diff --git a/README.md b/README.md index a4552909..8c8750e2 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,13 @@ The latest `scraper` workflow on GitHub should publish a `release-artifact` as w Various official sources are linked below, with the ones currently used marked with a † -- List of NEFT IFSCs from [RBI website][combined]† -- List of RTGS IFSCs from [RBI website][rtgs]† -- RBI maintains several lists: - - [List of banks in India](https://www.rbi.org.in/commonman/english/scripts/banksinindia.aspx) - - [Websites of banks in India](https://www.rbi.org.in/scripts/banklinks.aspx). - - [Bankwise Volumes in ECS/NEFT/RTGS/Mobile](https://www.rbi.org.in/Scripts/NEFTUserView.aspx?Id=166) - - [List of Banks permitted to provide Mobile Banking](https://www.rbi.org.in/Scripts/bs_viewcontent.aspx?Id=2463) +- List of NEFT IFSCs from [RBI website][combined]† +- List of RTGS IFSCs from [RBI website][rtgs]† +- RBI maintains several lists: +- [List of banks in India](https://www.rbi.org.in/commonman/english/scripts/banksinindia.aspx) +- [Websites of banks in India](https://www.rbi.org.in/scripts/banklinks.aspx). +- [Bankwise Volumes in ECS/NEFT/RTGS/Mobile](https://www.rbi.org.in/Scripts/NEFTUserView.aspx?Id=166) +- [List of Banks permitted to provide Mobile Banking](https://www.rbi.org.in/Scripts/bs_viewcontent.aspx?Id=2463) - NPCI website has several lists: - [NACH Live Members][ach]† - [RuPay Live Members](https://www.npci.org.in/what-we-do/rupay/live-members) @@ -39,15 +39,18 @@ Various official sources are linked below, with the ones currently used marked w SWIFT/BIC codes are supported for a few banks. ##### SBI -- https://sbi.co.in/web/nri/quick-links/swift-codes † -- https://sbi.co.in/documents/16012/263663/sbinri_merged_bran_swfcodet.xlsx † -- Branch codes from above are checked against the [SBI Branch Locator](https://www.sbi.co.in/web/home/locator/branch) to get the IFSC. + +- https://sbi.co.in/web/nri/quick-links/swift-codes † +- https://sbi.co.in/documents/16012/263663/sbinri_merged_bran_swfcodet.xlsx † +- Branch codes from above are checked against the [SBI Branch Locator](https://www.sbi.co.in/web/home/locator/branch) to get the IFSC. ##### PNB + - https://pnbindia.com/downloadprocess.aspx?fid=Zb7ImdUNlz9Ge73qn1nXQg== † - https://www.pnbindia.in/document/PNB-helpdesk/bic_code.pdf † ##### HDFC + - https://www.hdfcbank.com/nri-banking/correspondent-banks † ## Installation @@ -110,6 +113,10 @@ Finally, to use the top-of-trunk version of this repo, use the following command `go get github.com/razorpay/ifsc/v2@master` +## Python + +`pip install ifsc` + ## Support Matrix Only the latest version of each SDK is considered. @@ -120,6 +127,7 @@ Only the latest version of each SDK is considered. | Ruby | ✅ | ✅ | ✅ (✅) | ✅ | | Node.js | ✅ | ✅ | ❎ (❎) | ✅ | | Go | ✅ | ✅ | ✅ (✅) | ✅ | +| Python | ✅ | ✅ | ✅ (✅) | ✅ | ## API Documentation @@ -194,31 +202,31 @@ echo $client->lookupIFSC('https://ifsc.razorpay.com/HDFC0CAGSBK')->swift; // 'HD ### Node.js ```js -var ifsc = require('ifsc'); - -ifsc.validate('KKBK0000261'); // returns true -ifsc.validate('BOTM0XEEMRA'); // returns false - -ifsc.fetchDetails('KKBK0000261').then(function(res) { - console.log(res); - // { - // MICR: '560226263', - // BRANCH: 'THE AGS EMPLOYEES COOP BANK LTD', - // ADDRESS: 'SANGMESH BIRADAR BANGALORE', - // STATE: 'KARNATAKA', - // CONTACT: '+91802265658', - // UPI: true, - // RTGS: true, - // CITY: 'BANGALORE', - // CENTRE: 'BANGALORE URBAN', - // DISTRICT: 'BANGALORE URBAN', - // NEFT: true, - // IMPS: true, - // SWIFT: 'HDFCINBB', - // BANK: 'HDFC Bank', - // BANKCODE: 'HDFC', - // IFSC: 'HDFC0CAGSBK' - // } +var ifsc = require("ifsc"); + +ifsc.validate("KKBK0000261"); // returns true +ifsc.validate("BOTM0XEEMRA"); // returns false + +ifsc.fetchDetails("KKBK0000261").then(function (res) { + console.log(res); + // { + // MICR: '560226263', + // BRANCH: 'THE AGS EMPLOYEES COOP BANK LTD', + // ADDRESS: 'SANGMESH BIRADAR BANGALORE', + // STATE: 'KARNATAKA', + // CONTACT: '+91802265658', + // UPI: true, + // RTGS: true, + // CITY: 'BANGALORE', + // CENTRE: 'BANGALORE URBAN', + // DISTRICT: 'BANGALORE URBAN', + // NEFT: true, + // IMPS: true, + // SWIFT: 'HDFCINBB', + // BANK: 'HDFC Bank', + // BANKCODE: 'HDFC', + // IFSC: 'HDFC0CAGSBK' + // } }); console.log(ifsc.bank.PUNB); // prints PUNB @@ -409,6 +417,37 @@ func main() { ``` +### Python + +``` + +from ifsc import IFSC, Bank + +ifsc_obj = IFSC() + +ifsc_obj.validate("ABHY0065001") # returns True +ifsc_obj.validate_bank_code("ABHY") # returns True +ifsc_obj.get_bank_name("ABHY") # returns Abhyudaya Co-operative Bank +ifsc_obj.get_details("ABHY0065001") + +""" + +returns +{'ADDRESS': 'ABHYUDAYA BANK BLDG., B.NO.71, NEHRU NAGAR, KURLA (E), MUMBAI-400024', 'MICR': '400065001', 'DISTRICT': 'GREATER MUMBAI', 'ISO3166': 'IN-MH', 'STATE': 'MAHARASHTRA', 'BRANCH': 'Abhyudaya Co-operative Bank IMPS', 'CITY': 'MUMBAI', 'UPI': True, 'IMPS': True, 'SWIFT': None, 'RTGS': True, 'NEFT': True, 'CONTACT': '+912225260173', 'CENTRE': 'GREATER MUMBAI', 'BANK': 'Abhyudaya Co-operative Bank', 'BANKCODE': 'ABHY', 'IFSC': 'ABHY0065001'} + +""" + +bank_obj = Bank() +bank_obj.get_bank_details("ABHY") + +""" + +returns +{'code': 'ABHY', 'ifsc': 'ABHY0065001', 'micr': '400065001', 'iin': '607261', 'ach_credit': True, 'ach_debit': True, 'apbs': True, 'nach_debit': False, 'type': 'S-UCB', 'upi': True, 'name': 'Abhyudaya Co-operative Bank', 'bank_code': '065'} +""" + +``` + ### Code Notes Both the packages ship with a 300kb JSON file, that diff --git a/pyproject.toml b/pyproject.toml index e4547eda..cb9dcc44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,26 +1,13 @@ -[project] -name = "ifsc" -version = "0.0.1" -authors = [ - { name="sreevardhanreddi", email="sreevardhanreddi@gmail.com" }, -] -description = "This is part of the IFSC toolset released by Razorpay. You can find more details about the entire release at [ifsc.razorpay.com](https://ifsc.razorpay.com)." -readme = "README.md" -requires-python = ">=3.7" -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -dependencies = [ - "requests", -] - - -[project.urls] -"Homepage" = "https://github.com/razorpay/ifsc" -"Bug Tracker" = "https://github.com/razorpay/ifsc/issues" - [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" + +[tool.isort] +profile = "black" +line_length = 79 +honor_noqa = true +src_paths = ["boto3", "tests"] + +[tool.black] +line-length = 79 +skip_string_normalization = true diff --git a/setup.py b/setup.py index fec79291..c0b79068 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import shutil import glob import pathlib -from setuptools import setup, find_packages +from setuptools import setup BASE_DIR = pathlib.Path(__file__).parent.resolve() @@ -12,21 +12,39 @@ try: json_files = glob.glob(str(BASE_DIR / "src" / "*.json")) - os.makedirs(str(BASE_DIR / "src" / "python" / "data"), exist_ok=True) + os.makedirs(BASE_DIR / "src" / "python" / "data", exist_ok=True) for json_file in json_files: os.symlink( json_file, - str(BASE_DIR / "src" / "python" / "data" / os.path.basename(json_file)), + BASE_DIR / "src" / "python" / "data" / os.path.basename(json_file), ) setup( name="ifsc", version="0.0.1", + author="sreevardhanreddi", + author_email="sreevardhanreddi@gmail.com", + description="This is part of the IFSC toolset released by Razorpay. You can find more details about the entire release at [ifsc.razorpay.com](https://ifsc.razorpay.com).", + long_description=long_description, include_package_data=True, package_dir={"ifsc": "src/python"}, - package_data={"ifsc.data": ["*.json"]}, + package_data={"ifsc": ["data/*.json"]}, + python_requires=">=3.7", + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + project_urls={ + "Homepage": "https://github.com/razorpay/ifsc", + "Source": "https://github.com/razorpay/ifsc", + }, + install_requires=[ + "requests", + ], + test_suite="tests", zip_safe=False, ) except Exception as e: - print(e) + print("error: ", e, "during setup") finally: - shutil.rmtree(str(BASE_DIR / "src" / "python" / "data")) + shutil.rmtree(BASE_DIR / "src" / "python" / "data") diff --git a/src/python/__init__.py b/src/python/__init__.py index c83b798c..e47cfc24 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -1,3 +1,4 @@ from .ifsc import IFSC +from .bank import Bank -__all__ = ["IFSC"] +__all__ = ["IFSC", "Bank"] diff --git a/src/python/bank.py b/src/python/bank.py new file mode 100644 index 00000000..45fa6e89 --- /dev/null +++ b/src/python/bank.py @@ -0,0 +1,27 @@ +import json +from importlib.resources import files +from .ifsc import IFSC + +pkg_path = files("ifsc.data") + + +with open(pkg_path.joinpath("banks.json")) as f: + banks = json.load(f) + + +class Bank: + def __init__(self): + self.banks = banks + + def get_bank_details(self, bank_code): + if not bank_code in banks: + return + else: + data = banks[bank_code] + bank_name = IFSC().get_bank_name(bank_code) + if bank_name: + data["name"] = bank_name + if data["micr"] != "": + data["bank_code"] = data["micr"][3:6] + + return data diff --git a/src/python/ifsc.py b/src/python/ifsc.py index c480aeff..2e27bd41 100644 --- a/src/python/ifsc.py +++ b/src/python/ifsc.py @@ -50,22 +50,17 @@ def get_details(self, code): raise ValueError("Invalid IFSC code") data = {} - try: - res = requests.get(url) - if res.status_code == 200: - data = res.json() - elif res.status_code == 404: - raise ValueError("Invalid IFSC code") - else: - raise ValueError( - "IFSC API returned an invalid response for {}".format(code) - ) - except Exception as e: + res = requests.get(url) + if res.status_code == 200: + data = res.json() + elif res.status_code == 404: raise ValueError("Invalid IFSC code") + else: + raise ValueError("IFSC API returned an invalid response") return data - def get_bank_name(self, code: str) -> str: + def get_bank_name(self, code): if code in self.bank_names: return self.bank_names[code] @@ -78,8 +73,8 @@ def get_bank_name(self, code: str) -> str: else: raise ValueError("Invalid IFSC code") - def get_custom_sublet(self, code: str) -> str: - for key, value in self.custom_sublets: + def get_custom_sublet(self, code): + for key, value in self.custom_sublets.items(): if len(code) >= len(key) and code[: len(key)] == key: if value in self.bank_names: return self.bank_names[value] @@ -88,8 +83,5 @@ def get_custom_sublet(self, code: str) -> str: raise ValueError("Invalid IFSC code") - -if __name__ == "__main__": - ifsc = IFSC() - print(ifsc.get_bank_name("SBIN0000001")) - print(ifsc.get_details("SBIN0000001")) + def validate_bank_code(self, code): + return code in self.bank_names diff --git a/src/python/tests/__init__.py b/src/python/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/python/tests/test_bank.py b/src/python/tests/test_bank.py new file mode 100644 index 00000000..28eae7b7 --- /dev/null +++ b/src/python/tests/test_bank.py @@ -0,0 +1,43 @@ +from ifsc import Bank + + +class TestBanks: + @classmethod + def setup_class(cls): + cls.bank = Bank() + + @classmethod + def teardown_class(cls): + pass + + def test_get_bank_details(self): + pnb_bank = { + "name": "Punjab National Bank", + "bank_code": "024", + "code": "PUNB", + "type": "PSB", + "ifsc": "PUNB0244200", + "micr": "110024001", + "iin": "508568", + "apbs": True, + "ach_credit": True, + "ach_debit": True, + "nach_debit": True, + "upi": True, + } + fino_bank = { + "name": "Fino Payments Bank", + "bank_code": "099", + "code": "FINO", + "type": "PB", + "ifsc": "FINO0000001", + "micr": "990099909", + "iin": "608001", + "apbs": True, + "ach_credit": True, + "ach_debit": False, + "nach_debit": False, + "upi": True, + } + assert pnb_bank == self.bank.get_bank_details("PUNB") + assert fino_bank == self.bank.get_bank_details("FINO") diff --git a/src/python/tests/test_client.py b/src/python/tests/test_client.py new file mode 100644 index 00000000..39632869 --- /dev/null +++ b/src/python/tests/test_client.py @@ -0,0 +1,79 @@ +import json +from ifsc import IFSC +from unittest.mock import MagicMock, patch +import pytest + + +class TestIFSC: + @classmethod + def setup_class(cls): + cls.ifsc = IFSC() + + @classmethod + def teardown_class(cls): + pass + + @patch("ifsc.ifsc.requests") + def test_get_details_success(self, mock_requests): + mock_response = MagicMock() + + mock_response.status_code = 200 + mock_response.json.return_value = { + "MICR": "560226263", + "BRANCH": "THE AGS EMPLOYEES COOP BANK LTD", + "ADDRESS": "SANGMESH BIRADAR BANGALORE", + "STATE": "KARNATAKA", + "CONTACT": "+91802265658", + "UPI": True, + "RTGS": True, + "CITY": "BANGALORE", + "CENTRE": "BANGALORE URBAN", + "DISTRICT": "BANGALORE URBAN", + "NEFT": True, + "IMPS": True, + "SWIFT": "HDFCINBB", + "BANK": "HDFC Bank", + "BANKCODE": "HDFC", + "IFSC": "HDFC0CAGSBK", + } + mock_requests.get.return_value = mock_response + got = self.ifsc.get_details("HDFC0CAGSBK") + want = mock_response.json.return_value + assert want == got + assert want['MICR'] == got['MICR'] + assert want['BRANCH'] == got['BRANCH'] + assert want['ADDRESS'] == got['ADDRESS'] + assert want['STATE'] == got['STATE'] + assert want['CONTACT'] == got['CONTACT'] + assert want['UPI'] == got['UPI'] + assert want['RTGS'] == got['RTGS'] + assert want['CITY'] == got['CITY'] + assert want['CENTRE'] == got['CENTRE'] + assert want['DISTRICT'] == got['DISTRICT'] + assert want['NEFT'] == got['NEFT'] + assert want['IMPS'] == got['IMPS'] + assert want['SWIFT'] == got['SWIFT'] + assert want['BANK'] == got['BANK'] + assert want['BANKCODE'] == got['BANKCODE'] + assert want['IFSC'] == got['IFSC'] + + @patch("ifsc.ifsc.requests") + def test_get_details_not_found(self, mock_requests): + mock_response = MagicMock() + mock_response.status_code = 404 + mock_requests.get.return_value = mock_response + with pytest.raises(ValueError) as exc_info: + self.ifsc.get_details("HDFC0CAGSBK") + assert exc_info.value.args[0] == "Invalid IFSC code" + + @patch("ifsc.ifsc.requests") + def test_get_details_server_error(self, mock_requests): + mock_response = MagicMock() + mock_response.status_code = 429 + mock_requests.get.return_value = mock_response + ifsc_code = "HDFC0CAGSBK" + with pytest.raises(ValueError) as exc_info: + self.ifsc.get_details(ifsc_code) + assert ( + exc_info.value.args[0] == "IFSC API returned an invalid response" + ) diff --git a/src/python/tests/test_ifsc.py b/src/python/tests/test_ifsc.py new file mode 100644 index 00000000..3e87756e --- /dev/null +++ b/src/python/tests/test_ifsc.py @@ -0,0 +1,52 @@ +import json +from ifsc import IFSC + + +class TestIFSC: + @classmethod + def setup_class(cls): + cls.ifsc = IFSC() + cls.test_groups = json.loads( + open("./tests/validator_asserts.json").read() + ) + cls.sublets_fixtures = { + "SKUX": "IBKL0116SBK", + "SPTX": "IBKL0116SSB", + "VCOX": "IBKL0116VMC", + "AURX": "IBKL01192AC", + "NMCX": "IBKL0123NMC", + "MSSX": "IBKL01241MB", + "TNCX": "IBKL01248NC", + "URDX": "IBKL01263UC", + } + cls.custom_sublets_fixtures = { + "KSCB0006001": "Tumkur District Central Bank", + "WBSC0KPCB01": "Kolkata Police Co-operative Bank", + "YESB0ADB002": "Amravati District Central Co-operative Bank", + } + + @classmethod + def teardown_class(cls): + pass + + def test_validate_bank_names(self): + for bank_code, expected in self.sublets_fixtures.items(): + actual = self.ifsc.get_bank_name(bank_code) + expected = self.ifsc.get_bank_name(bank_code[0:4]) + assert actual == expected + + for bank_code, expected in self.custom_sublets_fixtures.items(): + actual = self.ifsc.get_bank_name(bank_code) + assert actual == expected + + def test_validator(self): + for test_cases in self.test_groups: + for code in self.test_groups[test_cases]: + assert ( + self.ifsc.validate(code) + == self.test_groups[test_cases][code] + ) + + def test_validate_bank_code(self): + assert self.ifsc.validate_bank_code("ABCX") == True + assert self.ifsc.validate_bank_code("Aaaa") == False