diff --git a/.github/workflows/bad-name-notifier-cd.yml b/.github/workflows/bad-name-notifier-cd.yml
index 396ba3fc3..d3271039b 100644
--- a/.github/workflows/bad-name-notifier-cd.yml
+++ b/.github/workflows/bad-name-notifier-cd.yml
@@ -25,6 +25,10 @@ on:
- "false"
- "true"
+permissions:
+ id-token: write
+ contents: write
+
jobs:
namex-bad-name-notifier-cd:
uses: bcgov/bcregistry-sre/.github/workflows/backend-job-cd.yaml@main
diff --git a/.github/workflows/bad-name-notifier-ci.yml b/.github/workflows/bad-name-notifier-ci.yml
index 29cfececa..a5f5c7b67 100644
--- a/.github/workflows/bad-name-notifier-ci.yml
+++ b/.github/workflows/bad-name-notifier-ci.yml
@@ -10,12 +10,14 @@ defaults:
shell: bash
working-directory: ./jobs/bad-name-notifier
+permissions:
+ id-token: write
+ contents: write
+
jobs:
namex-bad-name-notifier-ci:
uses: bcgov/bcregistry-sre/.github/workflows/backend-ci.yaml@main
with:
app_name: "namex-bad-name-notifier"
working_directory: "./jobs/bad-name-notifier"
- codecov_flag: "namexbadnamenotifier"
- skip_isort: "true"
- skip_black: "true"
\ No newline at end of file
+ codecov_flag: "namex-bad-name-notifier"
\ No newline at end of file
diff --git a/jobs/bad-name-notifier/.flake8 b/jobs/bad-name-notifier/.flake8
new file mode 100644
index 000000000..7bd56a393
--- /dev/null
+++ b/jobs/bad-name-notifier/.flake8
@@ -0,0 +1,8 @@
+[flake8]
+max-line-length = 80
+extend-ignore = E501
+exclude =
+ .venv,
+ .git,
+ migrations,
+ tests
diff --git a/jobs/bad-name-notifier/README.md b/jobs/bad-name-notifier/README.md
index 2d1614937..62826c395 100644
--- a/jobs/bad-name-notifier/README.md
+++ b/jobs/bad-name-notifier/README.md
@@ -18,5 +18,33 @@ Bad-Name-Notifier is a Python application designed to identify names with specia
1. Clone the repository:
```bash
- git clone https://github.com/your-repo/bad-name-notifier.git
- cd bad-name-notifier
+ git clone https://github.com/your-repo/namex.git
+ cd jobs/bad-name-notifier
+
+2 ### Install the dependencies
+```bash
+poetry install
+```
+
+3 ### Configure the .env
+(see .env.sample)
+
+```bash
+eval $(poetry env activate)
+```
+
+4 ### Run the job
+```bash
+python src/bad_name_notifier/app.py
+OR: ./run.sh
+```
+
+5 ### Run Linting
+```bash
+poetry run ruff check --fix
+```
+
+6 ### Run unit tests
+```bash
+poetry run pytest
+```
\ No newline at end of file
diff --git a/jobs/bad-name-notifier/__init__.py b/jobs/bad-name-notifier/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/jobs/bad-name-notifier/coverage.xml b/jobs/bad-name-notifier/coverage.xml
new file mode 100644
index 000000000..cb816fde9
--- /dev/null
+++ b/jobs/bad-name-notifier/coverage.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+ /mnt/c/dsk01/lab/temp/namex/jobs/bad-name-notifier/src
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jobs/bad-name-notifier/poetry.lock b/jobs/bad-name-notifier/poetry.lock
index c6b8fb1cb..9340c4003 100644
--- a/jobs/bad-name-notifier/poetry.lock
+++ b/jobs/bad-name-notifier/poetry.lock
@@ -229,6 +229,18 @@ files = [
{file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
]
+[[package]]
+name = "astroid"
+version = "3.3.11"
+description = "An abstract syntax tree for Python with inference support."
+optional = false
+python-versions = ">=3.9.0"
+groups = ["dev"]
+files = [
+ {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"},
+ {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"},
+]
+
[[package]]
name = "attrs"
version = "22.2.0"
@@ -248,6 +260,21 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-
tests = ["attrs[tests-no-zope]", "zope.interface"]
tests-no-zope = ["cloudpickle ; platform_python_implementation == \"CPython\"", "cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990) ; platform_python_implementation == \"CPython\"", "mypy (>=0.971,<0.990) ; platform_python_implementation == \"CPython\"", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version < \"3.11\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version < \"3.11\"", "pytest-xdist[psutil]", "pytest-xdist[psutil]"]
+[[package]]
+name = "autopep8"
+version = "2.3.2"
+description = "A tool that automatically formats Python code to conform to the PEP 8 style guide"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "autopep8-2.3.2-py2.py3-none-any.whl", hash = "sha256:ce8ad498672c845a0c3de2629c15b635ec2b05ef8177a6e7c91c74f3e9b51128"},
+ {file = "autopep8-2.3.2.tar.gz", hash = "sha256:89440a4f969197b69a995e4ce0661b031f455a9f776d2c5ba3dbd83466931758"},
+]
+
+[package.dependencies]
+pycodestyle = ">=2.12.0"
+
[[package]]
name = "blinker"
version = "1.9.0"
@@ -494,12 +521,131 @@ 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"
-groups = ["main"]
-markers = "platform_system == \"Windows\""
+groups = ["main", "dev", "test"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
+markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\"", test = "sys_platform == \"win32\""}
+
+[[package]]
+name = "coverage"
+version = "7.13.4"
+description = "Code coverage measurement for Python"
+optional = false
+python-versions = ">=3.10"
+groups = ["test"]
+files = [
+ {file = "coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415"},
+ {file = "coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b"},
+ {file = "coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a"},
+ {file = "coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f"},
+ {file = "coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012"},
+ {file = "coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def"},
+ {file = "coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256"},
+ {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda"},
+ {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92"},
+ {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c"},
+ {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58"},
+ {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9"},
+ {file = "coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf"},
+ {file = "coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95"},
+ {file = "coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053"},
+ {file = "coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11"},
+ {file = "coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa"},
+ {file = "coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7"},
+ {file = "coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00"},
+ {file = "coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef"},
+ {file = "coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903"},
+ {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f"},
+ {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299"},
+ {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505"},
+ {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6"},
+ {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9"},
+ {file = "coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9"},
+ {file = "coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f"},
+ {file = "coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f"},
+ {file = "coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459"},
+ {file = "coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3"},
+ {file = "coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634"},
+ {file = "coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3"},
+ {file = "coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa"},
+ {file = "coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3"},
+ {file = "coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a"},
+ {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7"},
+ {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc"},
+ {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47"},
+ {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985"},
+ {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0"},
+ {file = "coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246"},
+ {file = "coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126"},
+ {file = "coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d"},
+ {file = "coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9"},
+ {file = "coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac"},
+ {file = "coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea"},
+ {file = "coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b"},
+ {file = "coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525"},
+ {file = "coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242"},
+ {file = "coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148"},
+ {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a"},
+ {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23"},
+ {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80"},
+ {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea"},
+ {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a"},
+ {file = "coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d"},
+ {file = "coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd"},
+ {file = "coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af"},
+ {file = "coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d"},
+ {file = "coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12"},
+ {file = "coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b"},
+ {file = "coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9"},
+ {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092"},
+ {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9"},
+ {file = "coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26"},
+ {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2"},
+ {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940"},
+ {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c"},
+ {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0"},
+ {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b"},
+ {file = "coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9"},
+ {file = "coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd"},
+ {file = "coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997"},
+ {file = "coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601"},
+ {file = "coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689"},
+ {file = "coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c"},
+ {file = "coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129"},
+ {file = "coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552"},
+ {file = "coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a"},
+ {file = "coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356"},
+ {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71"},
+ {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5"},
+ {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98"},
+ {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5"},
+ {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0"},
+ {file = "coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb"},
+ {file = "coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505"},
+ {file = "coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2"},
+ {file = "coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056"},
+ {file = "coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc"},
+ {file = "coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9"},
+ {file = "coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf"},
+ {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55"},
+ {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72"},
+ {file = "coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a"},
+ {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6"},
+ {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3"},
+ {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750"},
+ {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39"},
+ {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0"},
+ {file = "coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea"},
+ {file = "coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932"},
+ {file = "coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b"},
+ {file = "coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0"},
+ {file = "coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91"},
+]
+
+[package.extras]
+toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
[[package]]
name = "cryptography"
@@ -602,6 +748,22 @@ files = [
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
]
+[[package]]
+name = "dill"
+version = "0.4.1"
+description = "serialize all of Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d"},
+ {file = "dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa"},
+]
+
+[package.extras]
+graph = ["objgraph (>=1.7.2)"]
+profile = ["gprof2dot (>=2022.7.29)"]
+
[[package]]
name = "dnspython"
version = "2.8.0"
@@ -657,6 +819,23 @@ files = [
[package.extras]
tests = ["coverage", "coveralls", "dill", "mock", "nose"]
+[[package]]
+name = "flake8"
+version = "7.3.0"
+description = "the modular source code checker: pep8 pyflakes and co"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"},
+ {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"},
+]
+
+[package.dependencies]
+mccabe = ">=0.7.0,<0.8.0"
+pycodestyle = ">=2.14.0,<2.15.0"
+pyflakes = ">=3.4.0,<3.5.0"
+
[[package]]
name = "flask"
version = "3.1.0"
@@ -1403,6 +1582,33 @@ typing-extensions = "*"
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-ruff"]
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.10"
+groups = ["test"]
+files = [
+ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"},
+ {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
+]
+
+[[package]]
+name = "isort"
+version = "5.13.2"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+groups = ["dev"]
+files = [
+ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+ {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.6)"]
+
[[package]]
name = "itsdangerous"
version = "2.2.0"
@@ -1826,6 +2032,18 @@ docs = ["alabaster (==0.7.12)", "sphinx (==4.4.0)", "sphinx-issues (==3.0.1)"]
lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)"]
tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"]
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+description = "McCabe checker, plugin for flake8"
+optional = false
+python-versions = ">=3.6"
+groups = ["dev"]
+files = [
+ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
+ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
+]
+
[[package]]
name = "multidict"
version = "6.7.0"
@@ -2212,7 +2430,7 @@ version = "21.3"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.6"
-groups = ["main"]
+groups = ["main", "test"]
files = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
@@ -2249,6 +2467,34 @@ files = [
{file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"},
]
+[[package]]
+name = "platformdirs"
+version = "4.9.2"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
+optional = false
+python-versions = ">=3.10"
+groups = ["dev"]
+files = [
+ {file = "platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd"},
+ {file = "platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291"},
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.9"
+groups = ["test"]
+files = [
+ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
+ {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["coverage", "pytest", "pytest-benchmark"]
+
[[package]]
name = "pronouncing"
version = "0.2.0"
@@ -2472,6 +2718,18 @@ files = [
[package.dependencies]
pyasn1 = ">=0.4.6,<0.7.0"
+[[package]]
+name = "pycodestyle"
+version = "2.14.0"
+description = "Python style guide checker"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"},
+ {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"},
+]
+
[[package]]
name = "pycountry"
version = "22.3.5"
@@ -2566,6 +2824,18 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
+[[package]]
+name = "pyflakes"
+version = "3.4.0"
+description = "passive checker of Python programs"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"},
+ {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"},
+]
+
[[package]]
name = "pyjwt"
version = "2.10.1"
@@ -2584,13 +2854,38 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
+[[package]]
+name = "pylint"
+version = "3.3.9"
+description = "python code static checker"
+optional = false
+python-versions = ">=3.9.0"
+groups = ["dev"]
+files = [
+ {file = "pylint-3.3.9-py3-none-any.whl", hash = "sha256:01f9b0462c7730f94786c283f3e52a1fbdf0494bbe0971a78d7277ef46a751e7"},
+ {file = "pylint-3.3.9.tar.gz", hash = "sha256:d312737d7b25ccf6b01cc4ac629b5dcd14a0fcf3ec392735ac70f137a9d5f83a"},
+]
+
+[package.dependencies]
+astroid = ">=3.3.8,<=3.4.0.dev0"
+colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
+dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""}
+isort = ">=4.2.5,<5.13 || >5.13,<7"
+mccabe = ">=0.6,<0.8"
+platformdirs = ">=2.2"
+tomlkit = ">=0.10.1"
+
+[package.extras]
+spelling = ["pyenchant (>=3.2,<4.0)"]
+testutils = ["gitpython (>3)"]
+
[[package]]
name = "pyparsing"
version = "3.2.5"
description = "pyparsing - Classes and methods to define and execute parsing grammars"
optional = false
python-versions = ">=3.9"
-groups = ["main"]
+groups = ["main", "test"]
files = [
{file = "pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e"},
{file = "pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6"},
@@ -2660,6 +2955,64 @@ setuptools = "*"
[package.extras]
solrcloud = ["kazoo (>=2.5.0)"]
+[[package]]
+name = "pytest"
+version = "7.4.4"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.7"
+groups = ["test"]
+files = [
+ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
+ {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=0.12,<2.0"
+
+[package.extras]
+testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-cov"
+version = "5.0.0"
+description = "Pytest plugin for measuring coverage."
+optional = false
+python-versions = ">=3.8"
+groups = ["test"]
+files = [
+ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
+ {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
+]
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
+
+[[package]]
+name = "pytest-mock"
+version = "3.15.1"
+description = "Thin-wrapper around the mock package for easier use with pytest"
+optional = false
+python-versions = ">=3.9"
+groups = ["test"]
+files = [
+ {file = "pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d"},
+ {file = "pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f"},
+]
+
+[package.dependencies]
+pytest = ">=6.2.5"
+
+[package.extras]
+dev = ["pre-commit", "pytest-asyncio", "tox"]
+
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
@@ -3317,6 +3670,18 @@ all = ["tornado (>=4.0)", "twisted"]
tornado = ["tornado (>=4.0)"]
twisted = ["twisted"]
+[[package]]
+name = "tomlkit"
+version = "0.14.0"
+description = "Style preserving TOML library"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680"},
+ {file = "tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064"},
+]
+
[[package]]
name = "toolz"
version = "0.12.1"
@@ -3600,4 +3965,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.12,<3.13"
-content-hash = "4f85181739af5db44985729cfbb939525851cef2ad30afc784bf16129c248869"
+content-hash = "1894ee6c94a2dc0684ec8f9e148a76fdd7b64a7ab28faa463481756cadee5903"
diff --git a/jobs/bad-name-notifier/pyproject.toml b/jobs/bad-name-notifier/pyproject.toml
index 6cce0cfb5..da4b6ad4b 100644
--- a/jobs/bad-name-notifier/pyproject.toml
+++ b/jobs/bad-name-notifier/pyproject.toml
@@ -1,15 +1,34 @@
[tool.poetry]
name = "bad-name-notifier"
-version = "0.2.0"
+version = "0.3.0"
description = "An app to detect and notify about bad names."
authors = ["Your Name "]
license = "Apache-2.0"
readme = "README.md"
-packages = [
- { include = "services", from = "src" },
- { include = "src" }
-]
+packages = [{ include = "*", from = "src" }]
[tool.poetry.dependencies]
python = ">=3.12,<3.13"
-namex = { git = "https://github.com/bcgov/namex.git", subdirectory = "api", branch = "main" }
\ No newline at end of file
+namex = { git = "https://github.com/bcgov/namex.git", subdirectory = "api", branch = "main" }
+[tool.poetry.group.test.dependencies]
+pytest = "^7.4.4"
+pytest-cov = "^5.0.0"
+pytest-mock = "^3.14.0"
+
+[tool.poetry.group.dev.dependencies]
+pylint = "^3.3.1"
+flake8 = "^7.1.1"
+isort = "^5.13.2"
+autopep8 = "^2.3.2"
+
+[tool.coverage.run]
+branch = true
+source = [
+ "namex_solr_importer",
+]
+omit = [
+ "*/.venv/*",
+ "*/__init__.py",
+ "*/app.py",
+ "*/services/*",
+]
\ No newline at end of file
diff --git a/jobs/bad-name-notifier/src/app.py b/jobs/bad-name-notifier/src/app.py
index 154ba3061..8a1ddc025 100644
--- a/jobs/bad-name-notifier/src/app.py
+++ b/jobs/bad-name-notifier/src/app.py
@@ -1,21 +1,26 @@
-from config import APP_CONFIG
+"""Application factory and configuration setup for the Bad Name Notifier service."""
+
from flask import Flask, current_app
+from structured_logging import StructuredLogging
+
+from config import get_named_config
from services.database_service import get_bad_names
from services.email_service import send_email_notification
-from structured_logging import StructuredLogging
+
def create_app(config_name="default"):
"""Creates and configures the Flask app."""
- app = Flask(__name__) # NOSONAR
- app.config.from_object(APP_CONFIG[config_name])
+ flask_app = Flask(__name__) # NOSONAR
+ flask_app.config.from_object(get_named_config(config_name))
# Configure Structured Logging
structured_logger = StructuredLogging()
- structured_logger.init_app(app)
- app.logger = structured_logger.get_logger()
+ structured_logger.init_app(flask_app)
+ flask_app.logger = structured_logger.get_logger()
+
+ return flask_app
- return app
def run_task():
"""Executes the task to query bad names and send an email."""
@@ -26,9 +31,10 @@ def run_task():
# Step 2: Send email
send_email_notification(bad_names)
current_app.logger.info("Notification sent successfully.")
- except Exception as e:
+ except Exception as e: # pylint: disable=broad-exception-caught
current_app.logger.error(f"An error occurred: {e}")
+
if __name__ == "__main__":
app = create_app()
with app.app_context(): # Ensures Flask app context is available
diff --git a/jobs/bad-name-notifier/src/config.py b/jobs/bad-name-notifier/src/config.py
index 95ffc42aa..b142376bc 100644
--- a/jobs/bad-name-notifier/src/config.py
+++ b/jobs/bad-name-notifier/src/config.py
@@ -1,36 +1,47 @@
+"""Application configuration module for the Bad Name Notifier service."""
+
+# pylint: disable=too-few-public-methods
import os
-from dotenv import find_dotenv, load_dotenv
+from dotenv import load_dotenv
# Get the project root directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Load the .env file from the project root
-load_dotenv(os.path.join(BASE_DIR, '.env'))
+load_dotenv(os.path.join(BASE_DIR, ".env"))
class Config:
"""Base configuration class."""
# Database Configuration
- DB_USER = os.getenv('DATABASE_USERNAME', 'postgres')
- DB_PASSWORD = os.getenv('DATABASE_PASSWORD', 'postgres')
- DB_NAME = os.getenv('DATABASE_NAME', 'unittesting')
- DB_HOST = os.getenv('DATABASE_HOST', 'localhost')
- DB_PORT = os.getenv('DATABASE_PORT', '5432')
-
- DB_SCHEMA = os.getenv('DATABASE_SCHEMA', 'public')
- DB_IP_TYPE = os.getenv('DATABASE_IP_TYPE', 'private')
- DB_OWNER = os.getenv('DATABASE_OWNER', 'postgres')
-
- if DB_INSTANCE_CONNECTION_NAME := os.getenv('DATABASE_INSTANCE_CONNECTION_NAME', None):
- SQLALCHEMY_DATABASE_URI = 'postgresql+pg8000://'
+ DB_USER = os.getenv("DATABASE_USERNAME", "")
+ DB_PASSWORD = os.getenv("DATABASE_PASSWORD", "")
+ DB_NAME = os.getenv("DATABASE_NAME", "")
+ DB_HOST = os.getenv("DATABASE_HOST", "")
+ DB_PORT = os.getenv("DATABASE_PORT", "5432")
+
+ DB_SCHEMA = os.getenv("DATABASE_SCHEMA", "")
+ DB_IP_TYPE = os.getenv("DATABASE_IP_TYPE", "")
+ DB_OWNER = os.getenv("DATABASE_OWNER", "")
+
+ if DB_INSTANCE_CONNECTION_NAME := os.getenv(
+ "DATABASE_INSTANCE_CONNECTION_NAME", None
+ ):
+ SQLALCHEMY_DATABASE_URI = "postgresql+pg8000://"
else:
- SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
+ SQLALCHEMY_DATABASE_URI = (
+ f"postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
+ )
# Email Configuration
EMAIL_RECIPIENTS = os.getenv("EMAIL_RECIPIENTS", "").split(",")
- NOTIFY_API_URL = f"{os.getenv("NOTIFY_API_URL", "") + os.getenv("NOTIFY_API_VERSION", "")}/notify"
+ NOTIFY_API_URL = (
+ f"{os.getenv('NOTIFY_API_URL', '')}"
+ f"{os.getenv('NOTIFY_API_VERSION', '')}"
+ "/notify"
+ )
ACCOUNT_SVC_AUTH_URL = os.getenv("KEYCLOAK_AUTH_TOKEN_URL", "")
ACCOUNT_SVC_CLIENT_ID = os.getenv("KEYCLOAK_CLIENT_ID", "")
ACCOUNT_SVC_CLIENT_SECRET = os.getenv("KEYCLOAK_CLIENT_SECRET", "")
@@ -42,18 +53,21 @@ class Config:
class DevConfig(Config):
"""Development-specific configuration."""
+
DEBUG = True
TESTING = False
class TestConfig(Config):
"""Testing-specific configuration."""
+
DEBUG = True
TESTING = True
class ProdConfig(Config):
"""Production-specific configuration."""
+
DEBUG = False
TESTING = False
diff --git a/jobs/bad-name-notifier/src/services/__init__.py b/jobs/bad-name-notifier/src/services/__init__.py
index 674a7149f..f708e7623 100644
--- a/jobs/bad-name-notifier/src/services/__init__.py
+++ b/jobs/bad-name-notifier/src/services/__init__.py
@@ -1,4 +1,4 @@
-# services/__init__.py
+"""Service layer package for the Bad Name Notifier application."""
from .database_service import get_bad_names
from .email_service import send_email_notification
diff --git a/jobs/bad-name-notifier/src/services/database_service.py b/jobs/bad-name-notifier/src/services/database_service.py
index ff11437e6..5c8b9b204 100644
--- a/jobs/bad-name-notifier/src/services/database_service.py
+++ b/jobs/bad-name-notifier/src/services/database_service.py
@@ -1,9 +1,28 @@
-from cloud_sql_connector import DBConfig, getconn
+"""Database service for retrieving bad names from the data source."""
+
from flask import current_app
+from google.cloud.sql.connector import Connector
from .utils import get_yesterday_utc_range
+def getconn():
+ """Create and return a database connection using Cloud SQL Connector.
+ Returns:
+ tuple: A tuple containing (connection, connector instance).
+ """
+ connector = Connector()
+ conn = connector.connect(
+ current_app.config.get("DB_INSTANCE_CONNECTION_NAME"),
+ "pg8000", # driver
+ user=current_app.config.get("DB_USER"),
+ db=current_app.config.get("DB_NAME"),
+ ip_type=current_app.config.get("DB_IP_TYPE", "private"),
+ enable_iam_auth=True, # 🔥 REQUIRED
+ )
+ return conn, connector
+
+
def get_bad_names() -> list[dict]:
"""
Fetch bad names from the database and return as a list of dictionaries.
@@ -14,22 +33,7 @@ def get_bad_names() -> list[dict]:
- Contains non-standard ASCII characters
- Event occurred after the start of yesterday UTC
"""
- schema = current_app.config.get("DB_SCHEMA", "public")
-
- db_config = DBConfig(
- instance_name=current_app.config.get("DB_INSTANCE_CONNECTION_NAME"),
- database=current_app.config.get("DB_NAME"),
- user=current_app.config.get("DB_USER"),
- ip_type=current_app.config.get("DB_IP_TYPE", "private"),
- schema=schema,
- pool_recycle=300,
- )
-
- # Ensure required fields are set
- if not all([db_config.instance_name, db_config.database, db_config.user]):
- raise ValueError("DBConfig fields instance_name, database, and user must be set")
-
- conn = getconn(db_config)
+ conn, connector = getconn()
cursor = conn.cursor()
start_of_yesterday_utc, start_of_today_utc = get_yesterday_utc_range()
@@ -73,3 +77,4 @@ def get_bad_names() -> list[dict]:
finally:
cursor.close()
conn.close()
+ connector.close()
diff --git a/jobs/bad-name-notifier/src/services/email_service.py b/jobs/bad-name-notifier/src/services/email_service.py
index f5cb1768e..07e23f05e 100644
--- a/jobs/bad-name-notifier/src/services/email_service.py
+++ b/jobs/bad-name-notifier/src/services/email_service.py
@@ -1,35 +1,48 @@
-import requests
+"""
+Email service module for sending bad name notifications
+via the Notify API.
+"""
+
from http import HTTPStatus
+
+import requests
from flask import current_app
-from .utils import get_yesterday_str, get_bearer_token
+
+from .utils import get_bearer_token, get_yesterday_str
+
def load_recipients():
"""Load recipients dynamically from an environment variable."""
recipients = current_app.config["EMAIL_RECIPIENTS"]
- return [r.strip('[]') for r in recipients] if isinstance(recipients, list) else []
+ return [r.strip("[]") for r in recipients] if isinstance(recipients, list) else []
+
def send_email(email: dict, token: str):
"""Send the email"""
current_app.logger.info(f"Send Email: {email}")
return requests.post(
- f'{current_app.config.get("NOTIFY_API_URL", "")}',
+ f"{current_app.config.get('NOTIFY_API_URL', '')}",
json=email,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
},
+ timeout=10, # ✅ prevents hanging forever
)
+
def send_email_notification(bad_names):
"""Sends an email notification with the bad names."""
# Dynamically load recipients
recipients = load_recipients()
- current_app.logger.info(f'recipients:{recipients}')
+ current_app.logger.info(f"recipients:{recipients}")
# Check if recipients list is empty
if not recipients:
current_app.logger.error("No recipients found in the configuration.")
- raise ValueError("Email recipients are not defined. Please check the configuration.")
+ raise ValueError(
+ "Email recipients are not defined. Please check the configuration."
+ )
# Create email content
body = generate_report_title() + "\n\n" + generate_report_body(bad_names)
@@ -52,28 +65,29 @@ def send_email_notification(bad_names):
f"Failed to send email. Status Code: {resp.status_code}, Response: {resp.text}"
)
+
def generate_report_title():
"""Generates an email title with yesterday's date."""
# Format the date as yyyy-mm-dd
yesterday = get_yesterday_str()
-
+
# Construct the email title
email_title = f"BAD CHARACTERS FOR {yesterday}"
-
+
return email_title
+
def generate_report_body(bad_names):
"""Formats the result into a table with headers and adds a total count at the end."""
# Table headers
title = f"{'NR Number':<15}{'Choice':<10}Name"
separator = "-" * len(title)
-
+
# Format each row
lines = [
- f"{row['nr_num']:<15}{row['choice']:<10}{row['name']}"
- for row in bad_names
+ f"{row['nr_num']:<15}{row['choice']:<10}{row['name']}" for row in bad_names
]
-
+
# Add total count at the end with an extra newline before the footer.
total_count = len(bad_names)
footer_separator = "-" * len(title)
diff --git a/jobs/bad-name-notifier/src/services/utils.py b/jobs/bad-name-notifier/src/services/utils.py
index f8d6cd663..d95a00ce1 100644
--- a/jobs/bad-name-notifier/src/services/utils.py
+++ b/jobs/bad-name-notifier/src/services/utils.py
@@ -1,30 +1,44 @@
+"""
+Utility functions for date calculations and authentication
+used by the Bad Name Notifier service.
+"""
+
from datetime import datetime, timedelta
+
import pytz
import requests
from cachetools import TTLCache, cached
from flask import current_app
+
def get_yesterday_str():
- # Calculate yesterday's date
+ """Calculate yesterday's date"""
yesterday = datetime.now() - timedelta(days=1)
-
+
# Format the date as yyyy-mm-dd
- return yesterday.strftime('%Y-%m-%d')
+ return yesterday.strftime("%Y-%m-%d")
+
def get_yesterday_utc_range():
- pacific = pytz.timezone('America/Los_Angeles')
+ """Calculate the start of today and yesterday in Pacific Time"""
+ pacific = pytz.timezone("America/Los_Angeles")
- # Calculate the start of today and yesterday in Pacific Time
- start_of_today_pacific = pacific.localize(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0))
+ start_of_today_pacific = pacific.localize(
+ datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
+ )
start_of_yesterday_pacific = start_of_today_pacific - timedelta(days=1)
# Convert to UTC
- start_of_today_utc = start_of_today_pacific.astimezone(pytz.utc).strftime('%Y-%m-%d %H:%M:%S')
- start_of_yesterday_utc = start_of_yesterday_pacific.astimezone(pytz.utc).strftime('%Y-%m-%d %H:%M:%S')
+ start_of_today_utc = start_of_today_pacific.astimezone(pytz.utc).strftime(
+ "%Y-%m-%d %H:%M:%S"
+ )
+ start_of_yesterday_utc = start_of_yesterday_pacific.astimezone(pytz.utc).strftime(
+ "%Y-%m-%d %H:%M:%S"
+ )
return start_of_yesterday_utc, start_of_today_utc
-@staticmethod
+
@cached(cache=TTLCache(maxsize=1, ttl=180))
def get_bearer_token():
"""Get a valid Bearer token for the service to use."""
@@ -38,10 +52,11 @@ def get_bearer_token():
data="grant_type=client_credentials",
headers={"content-type": "application/x-www-form-urlencoded"},
auth=(client_id, client_secret),
+ timeout=10, # ✅ prevents hanging
)
try:
return res.json().get("access_token")
- except Exception:
- current_app.logger.error(f"Error getting Bearer Token.")
- return None
\ No newline at end of file
+ except Exception: # pylint: disable=broad-exception-caught
+ current_app.logger.error("Error getting Bearer Token.")
+ return None
diff --git a/jobs/bad-name-notifier/tests/__init__.py b/jobs/bad-name-notifier/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/jobs/bad-name-notifier/tests/test_bad_name_notifier.py b/jobs/bad-name-notifier/tests/test_bad_name_notifier.py
new file mode 100644
index 000000000..df0589a52
--- /dev/null
+++ b/jobs/bad-name-notifier/tests/test_bad_name_notifier.py
@@ -0,0 +1,21 @@
+import sys
+import types
+
+# Mock 'config' so Python won't complain
+sys.modules["config"] = types.ModuleType("config")
+
+# Optional: add functions or attributes your code expects
+sys.modules["config"].get_named_config = lambda name: {}
+
+from app import create_app # noqa: E402
+
+
+def test_create_app_returns_flask_app():
+ app = create_app()
+ assert app is not None
+ assert hasattr(app, "run")
+
+
+def test_app_config_default():
+ app = create_app()
+ assert app.config is not None
diff --git a/jobs/bad-name-notifier/tests/test_config.py b/jobs/bad-name-notifier/tests/test_config.py
new file mode 100644
index 000000000..038210b1e
--- /dev/null
+++ b/jobs/bad-name-notifier/tests/test_config.py
@@ -0,0 +1,45 @@
+import sys
+
+import pytest
+
+
+def reload_config_module():
+ if "config" in sys.modules:
+ del sys.modules["config"]
+ import config as config
+
+ return config
+
+
+def test_sqlalchemy_uri_with_instance(monkeypatch):
+ monkeypatch.setenv("DATABASE_INSTANCE_CONNECTION_NAME", "test-instance")
+
+ config = reload_config_module()
+
+ assert config.Config.SQLALCHEMY_DATABASE_URI == "postgresql+pg8000://"
+
+
+def test_sqlalchemy_uri_without_instance(monkeypatch):
+ monkeypatch.delenv("DATABASE_INSTANCE_CONNECTION_NAME", raising=False)
+
+ config = reload_config_module()
+
+ assert "postgresql+pg8000://" in config.Config.SQLALCHEMY_DATABASE_URI
+
+
+def test_get_named_config_valid():
+ import config as config
+
+ assert config.get_named_config("development") == config.DevConfig
+ assert config.get_named_config("testing") == config.TestConfig
+ assert config.get_named_config("production") == config.ProdConfig
+ assert config.get_named_config("default") == config.ProdConfig
+
+
+def test_get_named_config_invalid():
+ import config as config
+
+ with pytest.raises(KeyError) as exc_info:
+ config.get_named_config("invalid-config")
+
+ assert "Unknown configuration" in str(exc_info.value)