Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] add api endpoints to support anonymous data check #32

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"editor.tabSize": 4,
"editor.formatOnSave": true,
"python.formatting.provider": "black",
"python.formatting.black": true,
"python.envFile": "${workspaceFolder}/.env",
"editor.codeActionsOnSave": {
"source.organizeImports": true
Expand Down
6 changes: 5 additions & 1 deletion .devcontainer/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ pandas==1.5.3
pandera==0.14.5
ipykernel
openpyxl
pytest
pytest
python-multipart
uvicorn
fastapi
httpx
8 changes: 4 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# path to original/raw NAICS excel file
NAICS_EXCEL_PATH = "./data/naics/raw/2-6 digit_2022_Codes.xlsx"
NAICS_EXCEL_PATH = "data/naics/raw/2-6 digit_2022_Codes.xlsx"
# path to parsed/filtered naics codes file
NAICS_CSV_PATH = "./data/naics/processed/2022_codes.csv"
NAICS_CSV_PATH = "data/naics/processed/2022_codes.csv"
# column header text containing naics code
NAICS_CODE_COL = "2022 NAICS US Code"
# column header text containing naics title/description
NAICS_TITLE_COL = "2022 NAICS US Title"

# path to original/raw NAICS zip file
CENSUS_RAW_ZIP_PATH = "./data/census/raw/CensusFlatFile2022.zip"
CENSUS_RAW_ZIP_PATH = "data/census/raw/CensusFlatFile2022.zip"
# path to parsed/filtered naics codes file
CENSUS_PROCESSED_CSV_PATH = "./data/census/processed/Census2022.processed.csv"
CENSUS_PROCESSED_CSV_PATH = "data/census/processed/Census2022.processed.csv"
# census file col indexes
CENSUS_STATE_COL_INDEX = 2
CENSUS_COUNTY_COL_INDEX = 3
Expand Down
5 changes: 5 additions & 0 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os
import sys

CURR_DIR = os.path.dirname(os.path.abspath(__file__)) # noqa: E402
sys.path.append(CURR_DIR) # noqa: E402
9 changes: 9 additions & 0 deletions src/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import os
import sys

CURR_DIR = os.path.dirname(os.path.abspath(__file__)) # noqa: E402
PARENT_DIR = os.path.dirname(CURR_DIR) # noqa: E402
ROOT_DIR = os.path.dirname(PARENT_DIR) # noqa: E402
sys.path.append(CURR_DIR) # noqa: E402
sys.path.append(PARENT_DIR) # noqa: E402
sys.path.append(ROOT_DIR) # noqa: E402
44 changes: 44 additions & 0 deletions src/api/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from routers import validate_router

from util.global_data import read_geoids, read_naics_codes
from validator.create_schemas import get_schemas

# HOWTO
# run: uvicorn main:app --host 0.0.0.0 --port 8080 --reload

app = FastAPI()
# get our schems
app.schemas = get_schemas(read_naics_codes(), read_geoids())


# custom exception handlers
@app.exception_handler(Exception)
async def api_exception_handler(request: Request, err: Exception):
"""
handle exception/error that is triggered by incorrect data format

Args:
request (Request): HTTP request
err (Exception): the actual exception object

Returns:
returns JSON Response with status code and message
"""
error_code = 400
error_msg = str(err)
if type(err) is ValueError:
error_code = 422
elif type(err) is AttributeError:
error_code = 500
error_msg = f"Internal Server Error. Type: {str(type(err))}"

return JSONResponse(
status_code=error_code,
content=[{"response": error_msg}],
)


# add anonymous validation endpoints
app.include_router(validate_router, prefix="/v1")
3 changes: 3 additions & 0 deletions src/api/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__all__ = ["validate_router"]

from .validate import router as validate_router
44 changes: 44 additions & 0 deletions src/api/routers/validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from fastapi import APIRouter, Request, UploadFile
from fastapi.responses import JSONResponse

from validator.create_schemas import validate_data_list, validate_raw_csv

# validate router
router = APIRouter()


@router.post("/validate/")
async def validate(request: Request, payload: dict):
"""
handle validate endpoint

Args:
request (Request): request object
payload (dict): data received from user

Returns:
JSON response
"""
phase1_schema = request.app.schemas[0]
phase2_schema = request.app.schemas[1]
response = validate_data_list(phase1_schema, phase2_schema, payload)
return JSONResponse(status_code=200, content=response)


@router.post("/upload/")
async def upload(request: Request, file: UploadFile):
"""
handle upload request

Args:
request (Request): request object
file (UploadFile): uploaded file

Returns:
JSON response
"""
contents = file.file.read()
phase1_schema = request.app.schemas[0]
phase2_schema = request.app.schemas[1]
response = validate_raw_csv(phase1_schema, phase2_schema, contents)
return JSONResponse(status_code=200, content=response)
10 changes: 7 additions & 3 deletions src/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import os
import sys

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

sys.path.append(os.path.join(ROOT_DIR, "validator"))
CURR_DIR = os.path.dirname(os.path.abspath(__file__)) # noqa: E402
PARENT_DIR = os.path.dirname(CURR_DIR) # noqa: E402
ROOT_DIR = os.path.dirname(PARENT_DIR) # noqa: E402
sys.path.append(CURR_DIR) # noqa: E402
sys.path.append(PARENT_DIR) # noqa: E402
sys.path.append(ROOT_DIR) # noqa: E402
sys.path.append(os.path.join(ROOT_DIR, "validator"))
Loading