diff --git a/.circleci/README.md b/.circleci/README.md new file mode 100644 index 00000000..81ab3e7c --- /dev/null +++ b/.circleci/README.md @@ -0,0 +1,40 @@ +# CircleCI Configuration +## Environment Variables +There is a minimum acceptable percentage for unit test code coverage that defaults to 85%. +You can modify this minimum by setting the MIN_COVERAGE_PERCENT environment variable to a +different value. + +# Using CircleCI local CLI + +## Install circleci local +Install on Linux or Mac with: +``` +curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | bash +``` + +Details and instructions for other platforms in the [CircleCI Docs](https://circleci.com/docs/2.0/local-cli/) + +## Validate the config.yml +Run this from the top level of the repo: +``` +circleci config validate +``` + +## Run the CircleCI Job locally +You can run a CircleCI job locally and avoid the change/commit/wait loop you need to +do if you want to actually run the changes on Circle. +This can save a lot of time when trying to debug an issue in CI. +``` +circleci local execute --job JOB_NAME +``` + +## Environment Variables +To run in the local CircleCI with specific environment variables, use the following pattern: + +``` +circleci local execute -e MIN_COVERAGE_PERCENT=15 --job unit-test +``` + +## CircleCI configuration +To get CircleCI to run tests, you have to configure the +project in the Circle web application https://app.circleci.com/ diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..f46bbb5f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,100 @@ +# See: https://circleci.com/docs/2.0/configuration-reference +version: 2.1 + +# See: https://circleci.com/docs/2.0/orb-intro/ +orbs: + # See the orb documentation here: https://circleci.com/developer/orbs/orb/circleci/python + python: circleci/python@1.2 + +# See: https://circleci.com/docs/2.0/configuration-reference/#jobs +jobs: + unit-test: + # These next lines defines a Docker executors: https://circleci.com/docs/2.0/executor-types/ + # A list of available CircleCI Docker convenience images are available here: https://circleci.com/developer/images/image/cimg/python + docker: + - image: cimg/python:3.8 + + steps: + - checkout + + - python/install-packages: + pkg-manager: pip + pip-dependency-file: requirements-test.txt + + - run: + name: Run tests, save a coverage report, and save coverage percentage + command: | + pytest --cov=. --cov-report=html --cov-report=term | tee pytest.out + export NEW_PERCENT=$(cat pytest.out | grep TOTAL | awk '{print $4}' | grep -oE "[0-9]+" ) + echo ${NEW_PERCENT} > htmlcov/total_percent.txt + echo Total code coverage percentage is ${NEW_PERCENT}% + + - store_artifacts: + path: htmlcov + + - run: + name: Compare the actual code coverage to the minimum target coverage + command: | + export NEW_PERCENT=$(cat htmlcov/total_percent.txt) + echo Comparing the current code coverage percentage of ${NEW_PERCENT}% to the target of ${MIN_COVERAGE_PERCENT-85}% + if [ "${NEW_PERCENT}" -lt ${MIN_COVERAGE_PERCENT-85} ]; then + echo The total code coverage percentage of ${NEW_PERCENT}% is below the minimum of ${MIN_COVERAGE_PERCENT-85}% + echo If you would like to modify the minimum coverage, set the MIN_COVERAGE_PERCENT environment variable + exit 1 + fi + echo Coverage is good. + + lint: + docker: + - image: cimg/python:3.8 + + steps: + - checkout + + - python/install-packages: + pkg-manager: pip + pip-dependency-file: requirements-test.txt + + - run: + name: Run lint + command: flake8 . + + + dependency-check: + docker: + - image: cimg/python:3.8 + + steps: + - checkout + + - python/install-packages: + pkg-manager: pip + pip-dependency-file: requirements-test.txt + + - run: + name: Run depency check + command: | + export today=$(date "+%Y-%m-%d") + + # gather up the -i ignore IDs fro safety check + export ignores=$( + grep -vE "^\s*#" .safety.dependency.ignore | # print out any non-comment line + grep "[0-9]" | # filter out any line that doesn't have a number in it + awk -v "today=${today}" '{ if ($2 > today || $2 == "") print "-i", $1}' | # print any line with date after today + xargs echo # put all the output from previous command on one line + ) + export command="safety check -r requirements.txt --full-report $ignores" + + echo "----------------------------------------------------" + echo "If you need to modify the ignore list for the safety" + echo "check, edit .safety.dependency.ignore file" + echo "----------------------------------------------------" + eval $command + +# See: https://circleci.com/docs/2.0/configuration-reference/#workflows +workflows: + sample: + jobs: + - unit-test + - lint + - dependency-check \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md new file mode 100644 index 00000000..c8890c97 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue-template.md @@ -0,0 +1,18 @@ +--- +name: Issue template +about: Issue template for fecfile-validate +title: '' +labels: '' +assignees: '' + +--- + +### Business Reason ### + +As a [role], I will be able to [blank] so that I can [business reason] + +### Acceptance Criteria ### + +**If** [precedent] +**When** [action] +**Then** [result] diff --git a/.safety.dependency.ignore b/.safety.dependency.ignore new file mode 100644 index 00000000..223ee2c6 --- /dev/null +++ b/.safety.dependency.ignore @@ -0,0 +1,12 @@ +# Any vulnerability ID numbers listed in this file will be ignored when +# running the safety dependency check. Each line should have the ID number +# and a date. The ID will be ignored by the CI pipeline check unitl the date +# in YYYY-MM-DD format listed for that line. +# If no date is listed, the exception will never expire. (NOT RECOMMENDED) +# +# test +# Example: +# 40104 2022-01-15 +# +40104 2022-01-25 # gunicorn +40105 2022-01-25 # gunicorn diff --git a/api/tests.py b/api/tests.py index a5476977..238e01b2 100644 --- a/api/tests.py +++ b/api/tests.py @@ -1,34 +1,36 @@ -import os import json import unittest from validate import app +class BaseDatabaseTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + # app_step_3.config.update({ + # 'DATABASE_NAME': TESTING_DATABASE_NAME + # }) + # cls.db = sqlite3.connect(TESTING_DATABASE_NAME) + # cls.db.execute(CREATE_BOOK_TABLE_QUERY) + # cls.db.commit() + pass -# class BaseDatabaseTestCase(unittest.TestCase): -# @classmethod -# def setUpClass(cls): -# app_step_3.config.update({ -# 'DATABASE_NAME': TESTING_DATABASE_NAME -# }) -# cls.db = sqlite3.connect(TESTING_DATABASE_NAME) -# cls.db.execute(CREATE_BOOK_TABLE_QUERY) -# cls.db.commit() + @classmethod + def tearDownClass(cls): + # os.remove(TESTING_DATABASE_NAME) + pass -# @classmethod -# def tearDownClass(cls): -# os.remove(TESTING_DATABASE_NAME) - -# def setUp(self): -# self.app = self.APP.test_client() -# self.db.execute("DELETE FROM book;") -# self.db.commit() + def setUp(self): + # self.app = self.APP.test_client() + # self.db.execute("DELETE FROM book;") + # self.db.commit() + pass class Step3TestCase(BaseDatabaseTestCase): APP = app + # noinspection SpellCheckingInspection def test_validate_parameters(self): # Preconditions resp = self.app.get('/book') diff --git a/api/validate.py b/api/validate.py index 7cd53dff..aa303b94 100644 --- a/api/validate.py +++ b/api/validate.py @@ -1,86 +1,117 @@ +import copy +import csv import json -from flask import Flask, request, g -from .utils import json_response, JSON_MIME_TYPE import re -import csv -import copy from decimal import Decimal + +from flask import Flask, request + +from .utils import json_response + app = Flask(__name__) """ -************************************************* GLOBAL LISTS used ******************************************************************************************* +************************************ GLOBAL LISTS used *********************************************************** """ EXCLUDED_FROM_FIELD_CHECK = ['lineNumber', 'transactionTypeIdentifier', 'child'] -list_SA_similar_INDV_REC = ["INDV_REC", "PARTN_MEMO", "IK_REC", "REATT_FROM", "REATT_MEMO", "RET_REC", "EAR_REC", - "CON_EAR_UNDEP", "CON_EAR_DEP", "IND_RECNT_REC", "IND_NP_RECNT_ACC", "IND_NP_HQ_ACC", "IND_NP_CONVEN_ACC", - "IND_REC_NON_CONT_ACC", "JF_TRAN_IND_MEMO", "JF_TRAN_NP_RECNT_IND_MEMO", "JF_TRAN_NP_CONVEN_IND_MEMO", +list_sa_similar_indv_rec = ["INDV_REC", "PARTN_MEMO", "IK_REC", "REATT_FROM", "REATT_MEMO", "RET_REC", "EAR_REC", + "CON_EAR_UNDEP", "CON_EAR_DEP", "IND_RECNT_REC", "IND_NP_RECNT_ACC", "IND_NP_HQ_ACC", + "IND_NP_CONVEN_ACC", + "IND_REC_NON_CONT_ACC", "JF_TRAN_IND_MEMO", "JF_TRAN_NP_RECNT_IND_MEMO", + "JF_TRAN_NP_CONVEN_IND_MEMO", "JF_TRAN_NP_HQ_IND_MEMO", "EAR_REC_RECNT_ACC", "EAR_REC_CONVEN_ACC", "EAR_REC_HQ_ACC"] -list_SA_similar_PAR_CON = ["PARTN_REC", "TRIB_REC", "TRIB_NP_RECNT_ACC", "TRIB_NP_HQ_ACC", "TRIB_NP_CONVEN_ACC", - "BUS_LAB_NON_CONT_ACC", "JF_TRAN_TRIB_MEMO", "JF_TRAN_NP_RECNT_TRIB_MEMO", "JF_TRAN_NP_CONVEN_TRIB_MEMO", - "JF_TRAN_NP_HQ_TRIB_MEMO"] -list_SA_similar_COND_EARM_PAC = ["EAR_MEMO", "PAC_CON_EAR_UNDEP", "PAC_CON_EAR_DEP", "PAC_EAR_REC", "PAC_EAR_MEMO", "PARTY_IK_REC", "PAC_IK_REC", "PARTY_REC", - "PAC_REC", "PAC_NON_FED_REC", "PAC_NON_FED_RET", "PAC_RET", "PARTY_RET", "TRAN", "PARTY_RECNT_REC", "PAC_RECNT_REC", - "TRIB_RECNT_REC", "PARTY_NP_RECNT_ACC", "PAC_NP_RECNT_ACC", - "PARTY_NP_HQ_ACC", "PAC_NP_HQ_ACC", "PARTY_NP_CONVEN_ACC", "PAC_NP_CONVEN_ACC", "OTH_CMTE_NON_CONT_ACC", "IK_TRAN","IK_TRAN_FEA", - "JF_TRAN", "JF_TRAN_PARTY_MEMO", "JF_TRAN_PAC_MEMO", - "JF_TRAN_NP_RECNT_ACC", "JF_TRAN_NP_RECNT_PAC_MEMO", "JF_TRAN_NP_CONVEN_ACC", "JF_TRAN_NP_CONVEN_PAC_MEMO", "JF_TRAN_NP_HQ_ACC", - "JF_TRAN_NP_HQ_PAC_MEMO", "EAR_REC_RECNT_ACC_MEMO", "EAR_REC_CONVEN_ACC_MEMO", "EAR_REC_HQ_ACC_MEMO"] -list_SA_similar_OFFSET = ["OFFSET_TO_OPEX"] -list_SA_similar_OTH_REC = ["OTH_REC"] -list_SA_similar_REF_NFED_CAN = ["REF_TO_OTH_CMTE"] -list_SA_similar_REF_FED_CAN = ["REF_TO_FED_CAN"] - -list_SB_similar_IK_OUT = ["IK_OUT"] -list_SB_similar_IK_TF_OUT = ["IK_TRAN_OUT", "IK_TRAN_FEA_OUT"] -list_SB_similar_EAR_OUT = ["CON_EAR_UNDEP_MEMO", "CON_EAR_DEP_MEMO", "PAC_CON_EAR_UNDEP_MEMO", "PAC_CON_EAR_DEP_OUT"] -list_SB_similar_IK_OUT_PTY = ["PARTY_IK_OUT", "PAC_IK_OUT"] - -list_SB_similar_OPEX_REC = ['OPEXP', 'OPEXP_CC_PAY_MEMO', 'OPEXP_STAF_REIM', 'OPEXP_STAF_REIM_MEMO', 'OPEXP_PMT_TO_PROL_VOID', 'OTH_DISB', - 'OTH_DISB_CC_PAY_MEMO', 'OTH_DISB_STAF_REIM', 'OTH_DISB_STAF_REIM_MEMO', 'OPEXP_HQ_ACC_OP_EXP_NP', - 'OPEXP_CONV_ACC_OP_EXP_NP', 'OTH_DISB_NC_ACC', 'OTH_DISB_NC_ACC_CC_PAY_MEMO', 'OTH_DISB_NC_ACC_STAF_REIM', - 'OTH_DISB_NC_ACC_STAF_REIM_MEMO', 'OTH_DISB_NC_ACC_PMT_TO_PROL_VOID'] -list_SB_similar_OPEX_CC = ['OPEXP_CC_PAY','OPEXP_PMT_TO_PROL','OTH_DISB_CC_PAY','OTH_DISB_PMT_TO_PROL','OTH_DISB_RECNT', - 'OTH_DISB_NP_RECNT_ACC', 'OTH_DISB_NC_ACC_CC_PAY', 'OTH_DISB_NC_ACC_PMT_TO_PROL', 'OPEXP_HQ_ACC_TRIB_REF', - 'OPEXP_CONV_ACC_TRIB_REF', 'OTH_DISB_NP_RECNT_TRIB_REF'] -list_SB_similar_PAY_MEMO = ['OPEXP_PMT_TO_PROL_MEMO','REF_CONT_IND', 'REF_CONT_IND_VOID','OTH_DISB_PMT_TO_PROL_MEMO', - 'OTH_DISB_NC_ACC_PMT_TO_PROL_MEMO', 'OPEXP_HQ_ACC_IND_REF', 'OPEXP_CONV_ACC_IND_REF', 'OTH_DISB_NP_RECNT_IND_REF'] -list_SB_similar_OPEX_TRAN = ['TRAN_TO_AFFI', 'CONT_TO_OTH_CMTE', 'REF_CONT_PARTY', 'REF_CONT_PARTY_VOID', 'OPEXP_HQ_ACC_REG_REF', - 'OPEXP_CONV_ACC_REG_REF', 'OTH_DISB_NP_RECNT_REG_REF'] -list_SB_similar_NONFED_PAC_RFD = ['REF_CONT_PAC', 'REF_CONT_NON_FED'] -list_SB_similar_CONTR_CAND = ['CONT_TO_CAN', 'CONT_TO_OTH_CMTE_VOID'] -list_SB_similar_VOID_RFND_PAC = ['REF_CONT_PAC_VOID', 'REF_CONT_NON_FED_VOID'] -list_SB_similar_FEA_CC = ['FEA_CC_PAY', 'FEA_PAY_TO_PROL'] -list_SB_similar_FEA_CC_MEMO = ['FEA_CC_PAY_MEMO', 'FEA_STAF_REIM', 'FEA_STAF_REIM_MEMO', 'FEA_PAY_TO_PROL_VOID'] -list_SB_similar_FEA_PAY_MEMO = ['FEA_PAY_TO_PROL_MEMO'] - -list_f3x_total = list_SA_similar_INDV_REC + list_SA_similar_PAR_CON + list_SB_similar_OPEX_REC + list_SB_similar_IK_OUT + list_SB_similar_IK_TF_OUT -list_f3x_total += list_SB_similar_EAR_OUT + list_SA_similar_COND_EARM_PAC + list_SB_similar_IK_OUT_PTY + list_SA_similar_OFFSET + list_SA_similar_OTH_REC -list_f3x_total += list_SA_similar_REF_NFED_CAN + list_SA_similar_REF_FED_CAN + list_SB_similar_OPEX_CC + list_SB_similar_PAY_MEMO + list_SB_similar_OPEX_TRAN -list_f3x_total += list_SB_similar_NONFED_PAC_RFD + list_SB_similar_CONTR_CAND + list_SB_similar_VOID_RFND_PAC + list_SB_similar_FEA_CC -list_f3x_total += list_SB_similar_FEA_CC_MEMO + list_SB_similar_FEA_PAY_MEMO - -list_f3x_schedules = ['SA','SB'] +list_sa_similar_par_con = ["PARTN_REC", "TRIB_REC", "TRIB_NP_RECNT_ACC", "TRIB_NP_HQ_ACC", "TRIB_NP_CONVEN_ACC", + "BUS_LAB_NON_CONT_ACC", "JF_TRAN_TRIB_MEMO", "JF_TRAN_NP_RECNT_TRIB_MEMO", + "JF_TRAN_NP_CONVEN_TRIB_MEMO", + "JF_TRAN_NP_HQ_TRIB_MEMO"] +list_sa_similar_cond_earm_pac = ["EAR_MEMO", "PAC_CON_EAR_UNDEP", "PAC_CON_EAR_DEP", "PAC_EAR_REC", "PAC_EAR_MEMO", + "PARTY_IK_REC", "PAC_IK_REC", "PARTY_REC", + "PAC_REC", "PAC_NON_FED_REC", "PAC_NON_FED_RET", "PAC_RET", "PARTY_RET", "TRAN", + "PARTY_RECNT_REC", "PAC_RECNT_REC", + "TRIB_RECNT_REC", "PARTY_NP_RECNT_ACC", "PAC_NP_RECNT_ACC", + "PARTY_NP_HQ_ACC", "PAC_NP_HQ_ACC", "PARTY_NP_CONVEN_ACC", "PAC_NP_CONVEN_ACC", + "OTH_CMTE_NON_CONT_ACC", "IK_TRAN", "IK_TRAN_FEA", + "JF_TRAN", "JF_TRAN_PARTY_MEMO", "JF_TRAN_PAC_MEMO", + "JF_TRAN_NP_RECNT_ACC", "JF_TRAN_NP_RECNT_PAC_MEMO", "JF_TRAN_NP_CONVEN_ACC", + "JF_TRAN_NP_CONVEN_PAC_MEMO", "JF_TRAN_NP_HQ_ACC", + "JF_TRAN_NP_HQ_PAC_MEMO", "EAR_REC_RECNT_ACC_MEMO", "EAR_REC_CONVEN_ACC_MEMO", + "EAR_REC_HQ_ACC_MEMO"] +list_sa_similar_offset = ["OFFSET_TO_OPEX"] +list_sa_similar_oth_rec = ["OTH_REC"] +list_sa_similar_ref_nfed_can = ["REF_TO_OTH_CMTE"] +list_sa_similar_ref_fed_can = ["REF_TO_FED_CAN"] + +list_sb_similar_ik_out = ["IK_OUT"] +list_sb_similar_ik_tf_out = ["IK_TRAN_OUT", "IK_TRAN_FEA_OUT"] +list_sb_similar_ear_out = ["CON_EAR_UNDEP_MEMO", "CON_EAR_DEP_MEMO", "PAC_CON_EAR_UNDEP_MEMO", "PAC_CON_EAR_DEP_OUT"] +list_sb_similar_ik_out_pty = ["PARTY_IK_OUT", "PAC_IK_OUT"] + +list_sb_similar_opex_rec = ['OPEXP', 'OPEXP_CC_PAY_MEMO', 'OPEXP_STAF_REIM', 'OPEXP_STAF_REIM_MEMO', + 'OPEXP_PMT_TO_PROL_VOID', 'OTH_DISB', + 'OTH_DISB_CC_PAY_MEMO', 'OTH_DISB_STAF_REIM', 'OTH_DISB_STAF_REIM_MEMO', + 'OPEXP_HQ_ACC_OP_EXP_NP', + 'OPEXP_CONV_ACC_OP_EXP_NP', 'OTH_DISB_NC_ACC', 'OTH_DISB_NC_ACC_CC_PAY_MEMO', + 'OTH_DISB_NC_ACC_STAF_REIM', + 'OTH_DISB_NC_ACC_STAF_REIM_MEMO', 'OTH_DISB_NC_ACC_PMT_TO_PROL_VOID'] +list_sb_similar_opex_cc = ['OPEXP_CC_PAY', 'OPEXP_PMT_TO_PROL', 'OTH_DISB_CC_PAY', 'OTH_DISB_PMT_TO_PROL', + 'OTH_DISB_RECNT', + 'OTH_DISB_NP_RECNT_ACC', 'OTH_DISB_NC_ACC_CC_PAY', 'OTH_DISB_NC_ACC_PMT_TO_PROL', + 'OPEXP_HQ_ACC_TRIB_REF', + 'OPEXP_CONV_ACC_TRIB_REF', 'OTH_DISB_NP_RECNT_TRIB_REF'] +list_sb_similar_pay_memo = ['OPEXP_PMT_TO_PROL_MEMO', 'REF_CONT_IND', 'REF_CONT_IND_VOID', 'OTH_DISB_PMT_TO_PROL_MEMO', + 'OTH_DISB_NC_ACC_PMT_TO_PROL_MEMO', 'OPEXP_HQ_ACC_IND_REF', 'OPEXP_CONV_ACC_IND_REF', + 'OTH_DISB_NP_RECNT_IND_REF'] +list_sb_similar_opex_tran = ['TRAN_TO_AFFI', 'CONT_TO_OTH_CMTE', 'REF_CONT_PARTY', 'REF_CONT_PARTY_VOID', + 'OPEXP_HQ_ACC_REG_REF', + 'OPEXP_CONV_ACC_REG_REF', 'OTH_DISB_NP_RECNT_REG_REF'] +list_sb_similar_nonfed_pac_rfd = ['REF_CONT_PAC', 'REF_CONT_NON_FED'] +list_sb_similar_contr_cand = ['CONT_TO_CAN', 'CONT_TO_OTH_CMTE_VOID'] +list_sb_similar_void_rfnd_pac = ['REF_CONT_PAC_VOID', 'REF_CONT_NON_FED_VOID'] +list_sb_similar_fea_cc = ['FEA_CC_PAY', 'FEA_PAY_TO_PROL'] +list_sb_similar_fea_cc_memo = ['FEA_CC_PAY_MEMO', 'FEA_STAF_REIM', 'FEA_STAF_REIM_MEMO', 'FEA_PAY_TO_PROL_VOID'] +list_sb_similar_fea_pay_memo = ['FEA_PAY_TO_PROL_MEMO'] + +list_f3x_total = (list_sa_similar_indv_rec + list_sa_similar_par_con + list_sb_similar_opex_rec + + list_sb_similar_ik_out + list_sb_similar_ik_tf_out) +list_f3x_total += (list_sb_similar_ear_out + list_sa_similar_cond_earm_pac + list_sb_similar_ik_out_pty + + list_sa_similar_offset + list_sa_similar_oth_rec) +list_f3x_total += (list_sa_similar_ref_nfed_can + list_sa_similar_ref_fed_can + list_sb_similar_opex_cc + + list_sb_similar_pay_memo + list_sb_similar_opex_tran) +list_f3x_total += (list_sb_similar_nonfed_pac_rfd + list_sb_similar_contr_cand + list_sb_similar_void_rfnd_pac + + list_sb_similar_fea_cc) +list_f3x_total += (list_sb_similar_fea_cc_memo + list_sb_similar_fea_pay_memo) + +list_f3x_schedules = ['SA', 'SB'] dict_parent_child_association = { - "PARTN_REC":["PARTN_MEMO"], "IK_REC":["IK_OUT"], "REATT_FROM":["REATT_MEMO"], "EAR_REC":["EAR_MEMO"], "CON_EAR_DEP":["CON_EAR_DEP_MEMO"], - "CON_EAR_UNDEP":["CON_EAR_UNDEP_MEMO"], "PARTY_IK_REC":["PARTY_IK_OUT"], - "PAC_IK_REC":["PAC_IK_OUT"], "PAC_CON_EAR_DEP":["PAC_CON_EAR_DEP_OUT"], "PAC_CON_EAR_UNDEP":["PAC_CON_EAR_UNDEP_MEMO"], - "PAC_EAR_REC":["PAC_EAR_MEMO"], "JF_TRAN":["JF_TRAN_PAC_MEMO", "JF_TRAN_IND_MEMO", "JF_TRAN_PARTY_MEMO", "JF_TRAN_TRIB_MEMO"], - "IK_TRAN":["IK_TRAN_OUT"], "IK_TRAN_FEA":["IK_TRAN_FEA_OUT"], - "JF_TRAN_NP_RECNT_ACC":["JF_TRAN_NP_RECNT_TRIB_MEMO", "JF_TRAN_NP_RECNT_IND_MEMO", "JF_TRAN_NP_RECNT_PAC_MEMO"], - "JF_TRAN_NP_CONVEN_ACC":["JF_TRAN_NP_CONVEN_TRIB_MEMO", "JF_TRAN_NP_CONVEN_PAC_MEMO", "JF_TRAN_NP_CONVEN_IND_MEMO"], - "JF_TRAN_NP_HQ_ACC":["JF_TRAN_NP_HQ_IND_MEMO", "JF_TRAN_NP_HQ_TRIB_MEMO", "JF_TRAN_NP_HQ_PAC_MEMO"], - "EAR_REC_RECNT_ACC":["EAR_REC_RECNT_ACC_MEMO"], "EAR_REC_CONVEN_ACC":["EAR_REC_CONVEN_ACC_MEMO"], - "EAR_REC_HQ_ACC":["EAR_REC_HQ_ACC_MEMO"], "OPEXP_CC_PAY":["OPEXP_CC_PAY_MEMO"], "OPEXP_STAF_REIM":["OPEXP_STAF_REIM_MEMO"], - "OPEXP_PMT_TO_PROL":["OPEXP_PMT_TO_PROL_MEMO"], "OTH_DISB_CC_PAY":["OTH_DISB_CC_PAY_MEMO"], - "OTH_DISB_STAF_REIM":["OTH_DISB_STAF_REIM_MEMO"], "OTH_DISB_PMT_TO_PROL":["OTH_DISB_PMT_TO_PROL_MEMO"], - "OTH_DISB_NC_ACC_CC_PAY":["OTH_DISB_NC_ACC_CC_PAY_MEMO"], "OTH_DISB_NC_ACC_STAF_REIM":["OTH_DISB_NC_ACC_STAF_REIM_MEMO"], - "FEA_CC_PAY":["FEA_CC_PAY_MEMO"], "FEA_STAF_REIM":["FEA_STAF_REIM_MEMO"], "FEA_PAY_TO_PROL":["FEA_PAY_TO_PROL_MEMO"], - } + "PARTN_REC": ["PARTN_MEMO"], "IK_REC": ["IK_OUT"], "REATT_FROM": ["REATT_MEMO"], "EAR_REC": ["EAR_MEMO"], + "CON_EAR_DEP": ["CON_EAR_DEP_MEMO"], + "CON_EAR_UNDEP": ["CON_EAR_UNDEP_MEMO"], "PARTY_IK_REC": ["PARTY_IK_OUT"], + "PAC_IK_REC": ["PAC_IK_OUT"], "PAC_CON_EAR_DEP": ["PAC_CON_EAR_DEP_OUT"], + "PAC_CON_EAR_UNDEP": ["PAC_CON_EAR_UNDEP_MEMO"], + "PAC_EAR_REC": ["PAC_EAR_MEMO"], + "JF_TRAN": ["JF_TRAN_PAC_MEMO", "JF_TRAN_IND_MEMO", "JF_TRAN_PARTY_MEMO", "JF_TRAN_TRIB_MEMO"], + "IK_TRAN": ["IK_TRAN_OUT"], "IK_TRAN_FEA": ["IK_TRAN_FEA_OUT"], + "JF_TRAN_NP_RECNT_ACC": ["JF_TRAN_NP_RECNT_TRIB_MEMO", "JF_TRAN_NP_RECNT_IND_MEMO", "JF_TRAN_NP_RECNT_PAC_MEMO"], + "JF_TRAN_NP_CONVEN_ACC": ["JF_TRAN_NP_CONVEN_TRIB_MEMO", "JF_TRAN_NP_CONVEN_PAC_MEMO", + "JF_TRAN_NP_CONVEN_IND_MEMO"], + "JF_TRAN_NP_HQ_ACC": ["JF_TRAN_NP_HQ_IND_MEMO", "JF_TRAN_NP_HQ_TRIB_MEMO", "JF_TRAN_NP_HQ_PAC_MEMO"], + "EAR_REC_RECNT_ACC": ["EAR_REC_RECNT_ACC_MEMO"], "EAR_REC_CONVEN_ACC": ["EAR_REC_CONVEN_ACC_MEMO"], + "EAR_REC_HQ_ACC": ["EAR_REC_HQ_ACC_MEMO"], "OPEXP_CC_PAY": ["OPEXP_CC_PAY_MEMO"], + "OPEXP_STAF_REIM": ["OPEXP_STAF_REIM_MEMO"], + "OPEXP_PMT_TO_PROL": ["OPEXP_PMT_TO_PROL_MEMO"], "OTH_DISB_CC_PAY": ["OTH_DISB_CC_PAY_MEMO"], + "OTH_DISB_STAF_REIM": ["OTH_DISB_STAF_REIM_MEMO"], "OTH_DISB_PMT_TO_PROL": ["OTH_DISB_PMT_TO_PROL_MEMO"], + "OTH_DISB_NC_ACC_CC_PAY": ["OTH_DISB_NC_ACC_CC_PAY_MEMO"], + "OTH_DISB_NC_ACC_STAF_REIM": ["OTH_DISB_NC_ACC_STAF_REIM_MEMO"], + "FEA_CC_PAY": ["FEA_CC_PAY_MEMO"], "FEA_STAF_REIM": ["FEA_STAF_REIM_MEMO"], + "FEA_PAY_TO_PROL": ["FEA_PAY_TO_PROL_MEMO"], +} """ -************************************************* Functions to check if fields exist in JSON ******************************************************************* +*************************** Functions to check if fields exist in JSON ******************************************** """ + + def check_null_value(check_value): try: if check_value in ["none", "null", " ", ""]: @@ -89,7 +120,8 @@ def check_null_value(check_value): return check_value except Exception as e: raise Exception('check_null_value function is throwing an error: ' + str(e)) - + + def length(value): try: if value in None: @@ -98,15 +130,20 @@ def length(value): return len(value) except Exception as e: raise Exception('length function is throwing an error: ' + str(e)) + + """ -************************************************* Functions to check the type of the field ******************************************************************* +********************************* Functions to check the type of the field ***************************************** """ + + def capital_alpha_numeric(value): try: return not bool(re.match('^[\x20-\x60\x7B-\x7D]+$', value)) except Exception as e: raise Exception('capital_alpha_numeric function is throwing an error: ' + str(e)) + def alpha_numeric(value): try: return not bool(re.match('^[\x20-\x7E]+$', value)) @@ -117,111 +154,128 @@ def alpha_numeric(value): except Exception as e: raise Exception('alpha_numeric function is throwing an error: ' + str(e)) + def alpha(value): try: return not bool(re.match('^[a-zA-Z]+$', value)) except Exception as e: raise Exception('alpha function is throwing an error: ' + str(e)) + def numeric(value): try: return not bool(re.match('^[0-9]+$', value)) except Exception as e: raise Exception('numeric function is throwing an error: ' + str(e)) + def amount(value): try: return not bool(re.match('^-?[0-9.]+$', str(value))) except Exception as e: raise Exception('amount function is throwing an error: ' + str(e)) + def date_check(value): try: return not bool(re.match('^-?[0-9/]+$', str(value))) except Exception as e: - raise Exception('amount function is throwing an error: ' + str(e)) + raise Exception('amount function is throwing an error: ' + str(e)) + """ -************************************************* Function used to find the file based on the transactionTypeIdentifier ******************************************************************* +********************* Function used to find the file based on the transactionTypeIdentifier *************************** """ -def json_file_name(transactionTypeIdentifier): + + +# TODO: This function is too complex. Requires refactoring. +# flake8 C901 (complexity limit) is disabled until this is resolved. +def json_file_name(transaction_type_identifier): # noqa: C901 try: error_flag = False file_name = 'api/rules/' - if transactionTypeIdentifier in list_SA_similar_INDV_REC: - file_name += 'SA_INDV_REC.json' - elif transactionTypeIdentifier in list_SA_similar_PAR_CON: + if transaction_type_identifier in list_sa_similar_indv_rec: + file_name += 'SA_INDV_REC.json' + elif transaction_type_identifier in list_sa_similar_par_con: file_name += 'SA_PAR_CON.json' - elif transactionTypeIdentifier in list_SA_similar_COND_EARM_PAC: + elif transaction_type_identifier in list_sa_similar_cond_earm_pac: file_name += 'SA_COND_EARM_PAC.json' - elif transactionTypeIdentifier in list_SA_similar_OFFSET: + elif transaction_type_identifier in list_sa_similar_offset: file_name += 'SA_OFFSET.json' - elif transactionTypeIdentifier in list_SA_similar_OTH_REC: + elif transaction_type_identifier in list_sa_similar_oth_rec: file_name += 'SA_OTH_REC.json' - elif transactionTypeIdentifier in list_SA_similar_REF_NFED_CAN: + elif transaction_type_identifier in list_sa_similar_ref_nfed_can: file_name += 'SA_REF_NFED_CAN.json' - elif transactionTypeIdentifier in list_SA_similar_REF_FED_CAN: + elif transaction_type_identifier in list_sa_similar_ref_fed_can: file_name += 'SA_REF_FED_CAN.json' - elif transactionTypeIdentifier in list_SB_similar_IK_OUT: + elif transaction_type_identifier in list_sb_similar_ik_out: file_name += 'SB_IK_OUT.json' - elif transactionTypeIdentifier in list_SB_similar_IK_TF_OUT: + elif transaction_type_identifier in list_sb_similar_ik_tf_out: file_name += 'SB_IK_TF_OUT.json' - elif transactionTypeIdentifier in list_SB_similar_EAR_OUT: + elif transaction_type_identifier in list_sb_similar_ear_out: file_name += 'SB_EARM_OUT.json' - elif transactionTypeIdentifier in list_SB_similar_IK_OUT_PTY: + elif transaction_type_identifier in list_sb_similar_ik_out_pty: file_name += 'SB_IK_OUT_PTY.json' - elif transactionTypeIdentifier in list_SB_similar_OPEX_REC: + elif transaction_type_identifier in list_sb_similar_opex_rec: file_name += 'SB_OP_EXP.json' - elif transactionTypeIdentifier in list_SB_similar_OPEX_CC: + elif transaction_type_identifier in list_sb_similar_opex_cc: file_name += 'SB_OPEX_CC.json' - elif transactionTypeIdentifier in list_SB_similar_PAY_MEMO: + elif transaction_type_identifier in list_sb_similar_pay_memo: file_name += 'SB_PAY_MEMO.json' - elif transactionTypeIdentifier in list_SB_similar_OPEX_TRAN: + elif transaction_type_identifier in list_sb_similar_opex_tran: file_name += 'SB_OPEX_TRAN.json' - elif transactionTypeIdentifier in list_SB_similar_NONFED_PAC_RFD: + elif transaction_type_identifier in list_sb_similar_nonfed_pac_rfd: file_name += 'SB_NONFED_PAC_RFD.json' - elif transactionTypeIdentifier in list_SB_similar_CONTR_CAND: + elif transaction_type_identifier in list_sb_similar_contr_cand: file_name += 'SB_CONTR_CAND.json' - elif transactionTypeIdentifier in list_SB_similar_VOID_RFND_PAC: + elif transaction_type_identifier in list_sb_similar_void_rfnd_pac: file_name += 'SB_VOID_RFND_PAC.json' - elif transactionTypeIdentifier in list_SB_similar_FEA_CC: + elif transaction_type_identifier in list_sb_similar_fea_cc: file_name += 'SB_FEA_CC.json' - elif transactionTypeIdentifier in list_SB_similar_FEA_CC_MEMO: + elif transaction_type_identifier in list_sb_similar_fea_cc_memo: file_name += 'SB_FEA_CC_MEMO.json' - elif transactionTypeIdentifier in list_SB_similar_FEA_PAY_MEMO: + elif transaction_type_identifier in list_sb_similar_fea_pay_memo: file_name += 'SB_FEA_PAY_MEMO.json' else: error_flag = True error_string = ', '.join(list_f3x_total) - file_name = 'transactionTypeIdentifier should be limited to these values: [' + error_string + ']. Input Received: ' + transactionTypeIdentifier + file_name = ('transactionTypeIdentifier should be limited to these values: [' + error_string + + ']. Input Received: ' + transaction_type_identifier) return error_flag, file_name except Exception as e: raise Exception('json_file_name function is throwing an error: ' + str(e)) + """ -************************************************* Function used to define the json format of the individual error/warning message ******************************************************************* +********* Function used to define the json format of the individual error/warning message ************************* """ + + def error_json_template(message_type, message, field_name, field_value, transaction_id): try: errormessage = message_type + "Message" errornumber = message_type + "Number" output_dict = {errormessage: message, - errornumber: "not yet implemented", - "tranId": transaction_id, - "fieldName": field_name, - "fieldValue": field_value} + errornumber: "not yet implemented", + "tranId": transaction_id, + "fieldName": field_name, + "fieldValue": field_value} return output_dict except Exception as e: raise Exception('error_json_template function is throwing an error: ' + str(e)) + """ -************************************************* Function which validates all the rules for form F99 using csv file ******************************************************************* +************** Function which validates all the rules for form F99 using csv file *********************************** """ -def func_validate(field_lists, data): + + +# TODO: This function is too complex. Requires refactoring. +# flake8 C901 (complexity limit) is disabled until this is resolved. +def func_validate(field_lists, data): # noqa: C901 try: errors_list = [] warnings_list = [] - special_list = [] str_field_not_in_json = " field is not provided" str_field_not_in_format = " field is not in the format of " str_field_not_length = " field should not be greater than " @@ -258,15 +312,20 @@ def func_validate(field_lists, data): warnings_list = warnings_list + temp_list output = {'errors': errors_list, - 'warnings': warnings_list} + 'warnings': warnings_list} return output except Exception as e: raise Exception('func_validate function is throwing an error: ' + str(e)) + """ -************************************************* Function which validates all the rules for form F3x using json files ******************************************************************* +************* Function which validates all the rules for form F3x using json files *********************************** """ -def func_json_validate(data, form_type): + + +# TODO: This function is too complex. Requires refactoring. +# flake8 C901 (complexity limit) is disabled until this is resolved. +def func_json_validate(data, form_type): # noqa: C901 try: transaction_id = data.get('transactionId') output = {'errors': [], 'warnings': []} @@ -275,18 +334,19 @@ def func_json_validate(data, form_type): if file_flag: message = file_name message_type = "error" - dict_temp = error_json_template(message_type, message, "transactionTypeIdentifier", data.get('transactionTypeIdentifier'), transaction_id) + dict_temp = error_json_template(message_type, message, "transactionTypeIdentifier", + data.get('transactionTypeIdentifier'), transaction_id) output['errors'].append(dict_temp) else: with open(file_name) as json_file: load_json = json.load(json_file) - rules = copy.deepcopy(load_json) + rules = copy.deepcopy(load_json) for rule in rules: for field_name, field_rule in rule.items(): if field_name in data and check_null_value(data.get(field_name)): type_error_flag = False if field_rule.get('field_type') == 'A/N': - if field_name == 'transactionId': + if field_name == 'transactionId': if capital_alpha_numeric(data.get(field_name)): type_error_flag = True else: @@ -307,31 +367,40 @@ def func_json_validate(data, form_type): if type_error_flag: if field_name == 'transactionId': - message = field_name + " field is not in " + field_rule.get('field_type') + " format. In case of alphabets, only uppercase are allowed for this field." + message = (field_name + " field is not in " + + field_rule.get('field_type') + + " format. In case of alphabets, only uppercase are allowed for this field.") else: message = field_name + " field is not in " + field_rule.get('field_type') + " format" - message_type = "error" - dict_temp = error_json_template(message_type, message, field_name, data.get(field_name), transaction_id) + message_type = "error" + dict_temp = error_json_template(message_type, message, field_name, data.get(field_name), + transaction_id) output['errors'].append(dict_temp) - - if len(field_rule.get('value_check'))> 0: + + if len(field_rule.get('value_check')) > 0: if not data.get(field_name) in field_rule.get('value_check'): error_string = ', '.join(field_rule.get('value_check')) - message = field_name + " field is not within the following acceptable values: [" + error_string + "]" + message = (field_name + + " field is not within the following acceptable values: [" + + error_string + "]") message_type = "error" - dict_temp = error_json_template(message_type, message, field_name, data.get(field_name), transaction_id) + dict_temp = error_json_template(message_type, message, field_name, data.get(field_name), + transaction_id) output['errors'].append(dict_temp) if len(str(data.get(field_name))) > int(field_rule.get('field_size')): - message = field_name + " field has "+ str(len(data.get(field_name))) + " characters. Maximum expected characters are " + field_rule.get('field_size') + message = field_name + " field has " + str(len(data.get( + field_name))) + " characters. Maximum expected characters are " + field_rule.get( + 'field_size') message_type = "error" - dict_temp = error_json_template(message_type, message, field_name, data.get(field_name), transaction_id) + dict_temp = error_json_template(message_type, message, field_name, data.get(field_name), + transaction_id) output['errors'].append(dict_temp) else: if field_rule.get('message_type') in ["error", "warning"]: if not check_null_value(field_rule.get('additional_validation_key')): - validation_error_flag = True + validation_error_flag = True message = field_name + " field is missing" message_type = field_rule.get('message_type') else: @@ -342,43 +411,51 @@ def func_json_validate(data, form_type): if key in data and check_null_value(data.get(key)): if operator == ">": if Decimal(data.get(key)) > Decimal(value): - validation_error_flag = True - message = field_name + " field is mandatory as " + key + " " + operator + " " + value + validation_error_flag = True + message = (field_name + " field is mandatory as " + + key + " " + operator + " " + value) message_type = "error" if operator == "IND >": if data.get('entityType') == 'IND': if Decimal(data.get(key)) > Decimal(value): - validation_error_flag = True - message = field_name + " field is mandatory as " + key + " " + operator + " " + value + validation_error_flag = True + message = (field_name + " field is mandatory as " + key + " " + + operator + " " + value) message_type = "error" if operator == "not in": if not data.get(key) in value: - validation_error_flag = True + validation_error_flag = True error_value = ', '.join(value) - message = field_name + " field is mandatory as " + key + " " + operator + " [" + error_value + "]" + message = (field_name + " field is mandatory as " + key + " " + + operator + " [" + error_value + "]") message_type = "error" if operator == "in": if data.get(key) in value: - validation_error_flag = True + validation_error_flag = True error_value = ', '.join(value) - message = field_name + " field is mandatory as " + key + " " + operator + " [" + error_value + "]" + message = (field_name + " field is mandatory as " + key + " " + + operator + " [" + error_value + "]") message_type = "error" elif operator == "form": if form_type == value: - validation_error_flag = True - message = field_name + " field is mandatory for " + data.get('transactionTypeIdentifier') + " transaction type for " + value + " form" + validation_error_flag = True + message = field_name + " field is mandatory for " + data.get( + 'transactionTypeIdentifier') + " transaction type for " + value + " form" message_type = "error" else: - validation_error_flag = True - message = key + " field is required in the data to determine if " + field_name + " is mandatory or not" + validation_error_flag = True + message = (key + " field is required in the data to determine if " + + field_name + " is mandatory or not") message_type = "error" if validation_error_flag: - dict_temp = error_json_template(message_type, message, field_name, "none", transaction_id) + dict_temp = error_json_template(message_type, message, field_name, "none", + transaction_id) if message_type == "error": output['errors'].append(dict_temp) else: output['warnings'].append(dict_temp) - # Code to check if there are any additional fields in the data apart from the format specs and throwing an error if there are such fields. + # Code to check if there are any additional fields in the data apart from the + # format specs and throwing an error if there are such fields. # This has to be uncommented once JSON builder is implemented based on this logic. for key, value in data.items(): check_flag = True @@ -389,7 +466,8 @@ def func_json_validate(data, form_type): if not check_flag: break if check_flag: - message = key + " field is not expected for this transaction type code: " + data.get('transactionTypeIdentifier') + message = key + " field is not expected for this transaction type code: " + data.get( + 'transactionTypeIdentifier') message_type = "error" field_dict_temp = error_json_template(message_type, message, key, "none", transaction_id) output['errors'].append(field_dict_temp) @@ -397,52 +475,75 @@ def func_json_validate(data, form_type): except Exception as e: raise Exception('func_json_validate function is throwing an error: ' + str(e)) + def child_validation(parent, list_parent, form_type): try: output = {"errors": [], "warnings": []} - # validating if the transactionTypeIdentifier of a transaction can be a valid parent based on the established parent child relationships defined in dict_parent_child_association + # validating if the transactionTypeIdentifier of a transaction can be a valid parent + # based on the established parent child relationships defined in dict_parent_child_association if parent.get('transactionTypeIdentifier') in list_parent: for ch in parent.get('child'): - # validatiing if the child transactionTypeIdentifier is a part established parent child relationships for the respective parent transactionTypeIdentifier - if not ch.get('transactionTypeIdentifier') in dict_parent_child_association.get(parent.get('transactionTypeIdentifier')): - child_transaction_codes_string = ', '.join(dict_parent_child_association.get(parent.get('transactionTypeIdentifier'))) - message = ch.get('transactionTypeIdentifier') + " transaction type cannot be a child of "+ parent.get('transactionTypeIdentifier') + " as per rules in dict_parent_child_association dictionary definition. Expected values are: [" + child_transaction_codes_string + "]" + # validating if the child transactionTypeIdentifier is a part established + # parent child relationships for the respective parent transactionTypeIdentifier + if not ch.get('transactionTypeIdentifier') in dict_parent_child_association.get( + parent.get('transactionTypeIdentifier')): + child_transaction_codes_string = ', '.join( + dict_parent_child_association.get(parent.get('transactionTypeIdentifier'))) + message = (ch.get('transactionTypeIdentifier') + + " transaction type cannot be a child of " + + parent.get('transactionTypeIdentifier') + + " as per rules in dict_parent_child_association" + + " dictionary definition. Expected values are: [" + + child_transaction_codes_string + "]") message_type = "error" - dict_temp = error_json_template(message_type, message, "transactionTypeIdentifier", ch.get('transactionTypeIdentifier'), ch.get('transactionId')) + dict_temp = error_json_template(message_type, message, "transactionTypeIdentifier", + ch.get('transactionTypeIdentifier'), ch.get('transactionId')) output['errors'].append(dict_temp) # validating if child backReferenceTransactionIdNumber is same as parent transactionId if ch.get('backReferenceTransactionIdNumber') != parent.get('transactionId'): - message = "Child backReferenceTransactionIdNumber field should match parent transactionId:"+ parent.get('transactionId') + message = ("Child backReferenceTransactionIdNumber field should match parent transactionId:" + + parent.get('transactionId')) message_type = "error" - dict_temp = error_json_template(message_type, message, "backReferenceTransactionIdNumber", ch.get('backReferenceTransactionIdNumber'), ch.get('transactionId')) - output['errors'].append(dict_temp) + dict_temp = error_json_template(message_type, message, "backReferenceTransactionIdNumber", + ch.get('backReferenceTransactionIdNumber'), ch.get('transactionId')) + output['errors'].append(dict_temp) child_output = func_json_validate(ch, form_type) output['errors'].extend(child_output.get('errors')) output['warnings'].extend(child_output.get('warnings')) else: - message = parent.get('transactionTypeIdentifier') + " transaction type cannot have a child transaction as per rules in dict_parent_child_association dictionary definition" + message = (parent.get('transactionTypeIdentifier') + + " transaction type cannot have a child transaction as per" + + " rules in dict_parent_child_association dictionary definition") message_type = "error" - dict_temp = error_json_template(message_type, message, "transactionTypeIdentifier", parent.get('transactionTypeIdentifier'), parent.get('transactionId')) + dict_temp = error_json_template(message_type, message, "transactionTypeIdentifier", + parent.get('transactionTypeIdentifier'), parent.get('transactionId')) output['errors'].append(dict_temp) return output except Exception as e: raise Exception('child_validation function is throwing an error: ' + str(e)) - + + """ ****************************************************************************************************************************** VALIDATE API - SPRINT 8 - FNE 452 - BY PRAVEEN JINKA ****************************************************************************************************************************** """ -@app.route('/v1/validate', methods=['POST']) -def validate(): + +# TODO: This function is too complex. Requires refactoring. +# flake8 C901 (complexity limit) is disabled until this is resolved. +@app.route('/v1/validate', methods=['POST']) +def validate(): # noqa: C901 try: - if not 'form_type' in request.form: + if 'form_type' not in request.form: raise Exception('form_type input parameter is missing in request body') forms_list = ['F99', 'F3X'] if not request.form.get('form_type') in forms_list: error_string = ', '.join(forms_list) - raise Exception('form_type input parameter can only have the following values: [' + error_string + ']. Input Received: ' + request.form.get('form_type')) + raise Exception('form_type input parameter can only have the following values: [' + + error_string + + ']. Input Received: ' + + request.form.get('form_type')) try: if 'json_validate' in request.files: json_data = request.files['json_validate'] @@ -455,31 +556,30 @@ def validate(): raise Exception('Error while loading JSON file attachment on json_validate input parameter: ' + str(e)) errors = [] - error_message = " is required" warnings = [] - warnings_message = " is expected but not required" """ - ************************************************* FORM 99 Valdation Rules ******************************************************************* + **************************** FORM 99 Validation Rules ********************************************* """ if request.form.get('form_type') == "F99": with open('api/rules/F99.csv') as csvfile: fields = [] - readCSV = csv.reader(csvfile, delimiter=',') - for row in readCSV: + read_csv = csv.reader(csvfile, delimiter=',') + for row in read_csv: fields.append(row) output = func_validate(fields, data.get('data')) """ - ************************************************* FORM F3X Valdation Rules ******************************************************************* + **************************** FORM F3X Validation Rules ******************************************** """ if request.form.get('form_type') == "F3X": - list_parent= [] + list_parent = [] for parent, child in dict_parent_child_association.items(): list_parent.append(parent) - for schedule_list in list_f3x_schedules: + for schedule_list in list_f3x_schedules: if 'schedules' in data.get('data') and data.get('data').get('schedules'): - if schedule_list in data.get('data').get('schedules') and data.get('data').get('schedules').get(schedule_list): + if schedule_list in data.get('data').get('schedules') and data.get('data').get('schedules').get( + schedule_list): if len(data.get('data').get('schedules').get(schedule_list)) > 0: for sa in data.get('data').get('schedules').get(schedule_list): output = func_json_validate(sa, request.form.get('form_type')) @@ -491,12 +591,11 @@ def validate(): child_output = child_validation(sa, list_parent, request.form.get('form_type')) errors.extend(child_output.get('errors')) warnings.extend(child_output.get('warnings')) - result = {} - result['committeeId'] = data.get('data').get('committeeId') - result['form_type'] = request.form.get('form_type') - result['submissionId'] = 'Not yet Implemented' - result['totalErrors'] = len(errors) - result['totalWarnings'] = len(warnings) + result = {'committeeId': data.get('data').get('committeeId'), + 'form_type': request.form.get('form_type'), + 'submissionId': 'Not yet Implemented', + 'totalErrors': len(errors), + 'totalWarnings': len(warnings)} if len(errors) > 0: result['status'] = "VALIDATION FAILED" else: @@ -505,14 +604,15 @@ def validate(): error = json.dumps({'headers': data.get('header'), 'errors': output.get('errors'), 'warnings': output.get('warnings')}) - else: + else: error = json.dumps({'result': result, 'errors': errors, 'warnings': warnings}) - return json_response(error,200) + return json_response(error, 200) except Exception as e: - error = json.dumps({'errors': 'validate API is throwing an error: '+str(e)}) - return json_response(error, 400) + error = json.dumps({'errors': 'validate API is throwing an error: ' + str(e)}) + return json_response(error, 400) + """ ****************************************************************************************************************************** diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 00000000..d1f22af0 --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,10 @@ +Flask==1.0.0 +Flask-RESTful==0.3.5 +pytest +pytest-cov +psycopg2-binary==2.8.2 +retrying==1.3.3 +gunicorn==19.9.0 +flake8 +pep8-naming +safety \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 524ccd3c..d01464d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ Flask==1.0.0 Flask-RESTful==0.3.5 -pytest==3.0.7 psycopg2-binary==2.8.2 retrying==1.3.3 gunicorn==19.9.0 diff --git a/run.py b/run.py index c29273f1..997786b7 100644 --- a/run.py +++ b/run.py @@ -1,4 +1,3 @@ -import os import settings from api.validate import app diff --git a/settings.py b/settings.py index 7c7c1fe3..457dc7a6 100644 --- a/settings.py +++ b/settings.py @@ -5,5 +5,5 @@ LANGUAGE_CODE = 'en-us' -#TIME_ZONE = 'UTC' +# TIME_ZONE = 'UTC' TIME_ZONE = "America/New_York" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_validate.py b/tests/test_validate.py new file mode 100644 index 00000000..11bf39cb --- /dev/null +++ b/tests/test_validate.py @@ -0,0 +1,6 @@ +from api.validate import check_null_value + + +def test_check_null_value(): + assert check_null_value(7) == 7 + assert check_null_value("null") is None