From 55b74cb9474691605cb2b02ce203d6270ebececd Mon Sep 17 00:00:00 2001 From: davemfish Date: Wed, 6 Sep 2023 10:08:12 -0400 Subject: [PATCH 01/37] some cleanup in sql app --- README.md | 3 + scripts/lucode_explore.ipynb | 709 +++++++++++++++++++++++++++++++++++ server/sql_app/main.py | 113 ++---- 3 files changed, 745 insertions(+), 80 deletions(-) create mode 100644 scripts/lucode_explore.ipynb diff --git a/README.md b/README.md index 384d46b..f468a38 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,10 @@ $ docker compose up --build `http://localhost:8000/docs` ## Data Requirements +These are data used by the python worker. + local path which mounts in container - (bucket where source file can be found) +`appdata/lucodes_crosswalk.csv` `appdata/NLCD_2016_epsg3857.tif` (natcap-urban-online-datasets-**public**) `appdata/invest-data/CGIAR_et0_annual_epsg_3857.tif` (natcap-urban-online-datasets) `appdata/invest-data/OE_Bioregions_3857.shp` (natcap-urban-online-datasets) diff --git a/scripts/lucode_explore.ipynb b/scripts/lucode_explore.ipynb new file mode 100644 index 0000000..870a6d9 --- /dev/null +++ b/scripts/lucode_explore.ipynb @@ -0,0 +1,709 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "71a9445c-83fa-4c48-a96d-c33d5e7aa9d9", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import Counter\n", + "\n", + "import numpy\n", + "import pygeoprocessing\n", + "import pandas\n", + "import ipywidgets as widgets\n", + "from IPython.display import display" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "87c6dfbb-017f-4164-b9f9-edff36a823d2", + "metadata": {}, + "outputs": [], + "source": [ + "# ! mamba install jupyterlab_widgets" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac4bf3f9-3543-42da-8afe-56ea96a16262", + "metadata": {}, + "outputs": [], + "source": [ + "raster_path = 'H:/Shared drives/Online Urban ES Modeling Tool/Data from Chris/Combined NLCD NLUD Tree/overlay.tif'\n", + "csv_path = 'H:/Shared drives/Online Urban ES Modeling Tool/Data from Chris/Combined NLCD NLUD Tree/combined_structure.csv'" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d9b31d02-51dd-4664-a576-828ff2677c91", + "metadata": {}, + "outputs": [], + "source": [ + "df = pandas.read_csv(csv_path)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "718348a8-bce4-42e8-b356-1c15875232df", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
lucodecodenludnlud tier 1nlud tier 2nlud tier 3nlud colorsnlcdnlcd lulcnlcd colorstreetree canopy covertree colors
00111110111WaterNatural - areaLake#0070ff11Open Water#476BA00none#ffffff
11111111111WaterNatural - areaLake#0070ff11Open Water#476BA01low#e5f5e0
22111112111WaterNatural - areaLake#0070ff11Open Water#476BA02medium#a1d99b
33111113111WaterNatural - areaLake#0070ff11Open Water#476BA03high#31a354
44111120111WaterNatural - areaLake#0070ff12Perennial Ice/Snow#D1DDF90none#ffffff
\n", + "
" + ], + "text/plain": [ + " lucode code nlud nlud tier 1 nlud tier 2 nlud tier 3 nlud colors \\\n", + "0 0 111110 111 Water Natural - area Lake #0070ff \n", + "1 1 111111 111 Water Natural - area Lake #0070ff \n", + "2 2 111112 111 Water Natural - area Lake #0070ff \n", + "3 3 111113 111 Water Natural - area Lake #0070ff \n", + "4 4 111120 111 Water Natural - area Lake #0070ff \n", + "\n", + " nlcd nlcd lulc nlcd colors tree tree canopy cover tree colors \n", + "0 11 Open Water #476BA0 0 none #ffffff \n", + "1 11 Open Water #476BA0 1 low #e5f5e0 \n", + "2 11 Open Water #476BA0 2 medium #a1d99b \n", + "3 11 Open Water #476BA0 3 high #31a354 \n", + "4 12 Perennial Ice/Snow #D1DDF9 0 none #ffffff " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5c6872dd-7515-48e7-894a-9978d24eb369", + "metadata": {}, + "outputs": [], + "source": [ + "def count(c, block):\n", + " return c + Counter(block)\n", + "counts = pygeoprocessing.raster_reduce(count, (raster_path, 1), Counter())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5517d4f1-d9cd-45e8-8865-3beecc0552c3", + "metadata": {}, + "outputs": [], + "source": [ + "def unique(unique, block):\n", + " return unique.union(set(numpy.unique(block)))\n", + "value_set = pygeoprocessing.raster_reduce(unique, (raster_path, 1), set())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d17adcfc-b070-433c-862d-91dd4fc1f51d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1556" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(value_set)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "af753d10-29f7-48ff-b719-7f467d7f0350", + "metadata": {}, + "outputs": [], + "source": [ + "table = df.loc[df.lucode.isin(value_set)]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "11795382-4941-422f-87b0-9bb8195aa3e8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
lucodecodenludnlud tier 1nlud tier 2nlud tier 3nlud colorsnlcdnlcd lulcnlcd colorstreetree canopy covertree colors
00111110111WaterNatural - areaLake#0070ff11Open Water#476BA00none#ffffff
11111111111WaterNatural - areaLake#0070ff11Open Water#476BA01low#e5f5e0
22111112111WaterNatural - areaLake#0070ff11Open Water#476BA02medium#a1d99b
88111210111WaterNatural - areaLake#0070ff21Developed, Open Space#DDC9C90none#ffffff
99111211111WaterNatural - areaLake#0070ff21Developed, Open Space#DDC9C91low#e5f5e0
\n", + "
" + ], + "text/plain": [ + " lucode code nlud nlud tier 1 nlud tier 2 nlud tier 3 nlud colors \\\n", + "0 0 111110 111 Water Natural - area Lake #0070ff \n", + "1 1 111111 111 Water Natural - area Lake #0070ff \n", + "2 2 111112 111 Water Natural - area Lake #0070ff \n", + "8 8 111210 111 Water Natural - area Lake #0070ff \n", + "9 9 111211 111 Water Natural - area Lake #0070ff \n", + "\n", + " nlcd nlcd lulc nlcd colors tree tree canopy cover tree colors \n", + "0 11 Open Water #476BA0 0 none #ffffff \n", + "1 11 Open Water #476BA0 1 low #e5f5e0 \n", + "2 11 Open Water #476BA0 2 medium #a1d99b \n", + "8 21 Developed, Open Space #DDC9C9 0 none #ffffff \n", + "9 21 Developed, Open Space #DDC9C9 1 low #e5f5e0 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "35041cc5-d2a5-4a51-b417-33138bc5d638", + "metadata": {}, + "outputs": [], + "source": [ + "table.columns = [col.replace(' ', '_') for col in table.columns]" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "bdb0e3c1-7d29-4d94-bd9b-5d55ff06f7e4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['lucode', 'code', 'nlud', 'nlud_tier_1', 'nlud_tier_2', 'nlud_tier_3',\n", + " 'nlud_colors', 'nlcd', 'nlcd_lulc', 'nlcd_colors', 'tree',\n", + " 'tree_canopy_cover', 'tree_colors'],\n", + " dtype='object')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "6bb720a4-4373-4a25-9853-1b9fb7ec68d7", + "metadata": {}, + "outputs": [], + "source": [ + "table.to_csv('H:/Shared drives/Online Urban ES Modeling Tool/Data from Chris/Combined NLCD NLUD Tree/present_in_raster.csv', index=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7af7922e-e93d-4a41-9dd5-31e66b539c94", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "b616ef45-22f0-4f9b-a605-e38265c412d8", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dbd416350a6e4c9381f9155a4f22fc0d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Dropdown(options=(('Open Water', 11), ('Developed, High Intensity', 24), ('Cultivated Crops', 82), ('Woody Wet…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "nlcd_options = list(set([(name, value) for name, value in zip(table['nlcd lulc'], table['nlcd'])]))\n", + "nlcd = widgets.Dropdown(\n", + " options=nlcd_options\n", + ")\n", + "display(nlcd)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "78cd8bc0-9df4-4865-a43d-885ffd1b0734", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fe0ffa5e33cf420e9491731ff49cad67", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Dropdown(options=(('Conservation | Private easement | Wildlife conservation', 531), ('Water | Natural - area |…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "t = table.loc[table.nlcd == nlcd.value]\n", + "nlud_options = list(set([(' | '.join([name1, name2, name3]), value) for name1, name2, name3, value in zip(t['nlud tier 1'], t['nlud tier 2'], t['nlud tier 3'] , t['nlud'])]))\n", + "nlud = widgets.Dropdown(\n", + " options=nlud_options\n", + ")\n", + "display(nlud)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "bf3f8d0d-3f54-46cf-a842-a7e187bd5b36", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
lucodecodenludnlud tier 1nlud tier 2nlud tier 3nlud colorsnlcdnlcd lulcnlcd colorstreetree canopy covertree colors
14401440221110221Built-upCommercialOffice#df73ff11Open Water#476BA00none#ffffff
14411441221111221Built-upCommercialOffice#df73ff11Open Water#476BA01low#e5f5e0
14421442221112221Built-upCommercialOffice#df73ff11Open Water#476BA02medium#a1d99b
\n", + "
" + ], + "text/plain": [ + " lucode code nlud nlud tier 1 nlud tier 2 nlud tier 3 nlud colors \\\n", + "1440 1440 221110 221 Built-up Commercial Office #df73ff \n", + "1441 1441 221111 221 Built-up Commercial Office #df73ff \n", + "1442 1442 221112 221 Built-up Commercial Office #df73ff \n", + "\n", + " nlcd nlcd lulc nlcd colors tree tree canopy cover tree colors \n", + "1440 11 Open Water #476BA0 0 none #ffffff \n", + "1441 11 Open Water #476BA0 1 low #e5f5e0 \n", + "1442 11 Open Water #476BA0 2 medium #a1d99b " + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table.loc[(table.nlcd == nlcd.value) & (table.nlud == nlud.value)]" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "34ffe44a-7b95-4cda-a2fd-5e104d7af69d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "14" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "counts[1440]" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "0f3ddc57-0474-495a-bbaf-ca97b2f785b8", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'>' not supported between instances of 'Counter' and 'int'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[18], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mcounts\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m>\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1000\u001b[39;49m\n", + "\u001b[1;31mTypeError\u001b[0m: '>' not supported between instances of 'Counter' and 'int'" + ] + } + ], + "source": [ + "counts > 1000" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe45e36b-2eb3-42ce-860f-e764129a2c78", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/server/sql_app/main.py b/server/sql_app/main.py index d816d82..8c4c07d 100644 --- a/server/sql_app/main.py +++ b/server/sql_app/main.py @@ -18,12 +18,6 @@ from . import crud, models, schemas from .database import SessionLocal, engine - -# This will help with flexibility of where we store our files and DB -# When gathering URL result for frontend request build the URL with this: -WORKING_ENV = "/opt/appdata" -BASE_LULC = "NLCD_2016_epsg3857.tif" - logging.basicConfig( level=logging.DEBUG, format=( @@ -32,9 +26,10 @@ stream=sys.stdout) LOGGER = logging.getLogger(__name__) -# Create the db tables -models.Base.metadata.create_all(bind=engine) - +# This will help with flexibility of where we store our files and DB +# When gathering URL result for frontend request build the URL with this: +WORKING_ENV = "/opt/appdata" +BASE_LULC = "NLCD_2016_epsg3857.tif" # Create a queue that we will use to store our "workload". QUEUE = queue.PriorityQueue() @@ -67,7 +62,7 @@ "stats_under_parcel": "stats_under_parcel", } - +models.Base.metadata.create_all(bind=engine) # Normally you would probably initialize your db (create tables, etc) with # Alembic. Would also use Alembic for "migrations" (that's its main job). # A "migration" is the set of steps needed whenever you change the structure @@ -75,17 +70,12 @@ # in the db, add a new column, a new table, etc. app = FastAPI() - - -origins = [ - "http://localhost:3000", -] - +origins = ["http://localhost:3000"] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, - allow_methods=["*"], # despite this *, I could not get PATCH to pass CORS + allow_methods=["*"], # despite this *, PATCH does not pass CORS allow_headers=["*"], ) @@ -100,34 +90,31 @@ async def validation_exception_handler( return JSONResponse( content=content, status_code=HTTP_422_UNPROCESSABLE_ENTITY) -# We need to have an independent db session / connection (SessionLocal) per -# request, use the same session through all the request and then close it after -# the request is finished. - -# Then a new session will be created for the next request. - -# Our dependency will create a new SQLA SessionLocal that will be used in a -# single request, and then close it once the request is finished. # Dependency def get_db(): + # We need to have an independent db session / connection (SessionLocal) per + # request, use the same session through all the request and then close it after + # the request is finished. + + # Then a new session will be created for the next request. + + # Our dependency will create a new SQLA SessionLocal that will be used in a + # single request, and then close it once the request is finished. + # We are creating the db session before each request in the dependency with + # 'yield', and then closing it afterwards. + + # Then we can create the required dependency in the path operation function, + # to get that session directly. + + # With that, we can just call crud.get_user directly from inside of the path + # operation function and use that session. db = SessionLocal() try: yield db finally: db.close() -# We are creating the db session before each request in the dependency with -# 'yield', and then closing it afterwards. - -# Then we can create the required dependency in the path operation function, -# to get that session directly. - -# With that, we can just call crud.get_user directly from inside of the path -# operation function and use that session. -### - -### Session Endpoints ### @app.post("/sessions/", response_model=schemas.SessionResponse) def create_session(db: Session = Depends(get_db)): @@ -139,13 +126,6 @@ def create_session(db: Session = Depends(get_db)): return crud.create_session(db=db) -# Type annotations in the function arguments will give you editor support -# inside of your function, with error checks, completion, etc. -# So, with that type declaration, FastAPI gives you automatic request -# "parsing". With the same Python type declaration, FastAPI gives you data -# validation. All the data validation is performed under the hood by Pydantic, -# so you get all the benefits from it. - @app.get("/session/{session_id}", response_model=schemas.Session) def read_session(session_id: str, db: Session = Depends(get_db)): db_session = crud.get_session(db, session_id=session_id) @@ -154,8 +134,6 @@ def read_session(session_id: str, db: Session = Depends(get_db)): return db_session -### Study Area and Scenario Endpoints ### - @app.post("/study_area/{session_id}", response_model=schemas.StudyArea) def create_study_area( session_id: str, new_area: schemas.StudyAreaCreateRequest, @@ -171,7 +149,7 @@ def create_study_area( @app.get("/study_area/{session_id}/{study_area_id}", response_model=schemas.StudyArea) def get_study_area( - session_id: str, study_area_id: int, db: Session = Depends(get_db)): + session_id: str, study_area_id: int, db: Session = Depends(get_db)): # check that the session exists db_session = crud.get_session(db, session_id=session_id) if db_session is None: @@ -179,7 +157,7 @@ def get_study_area( db_study_area = crud.get_study_area(db, study_area_id=study_area_id) return db_study_area -# TODO: patch method blocked by CORS? fails preflight request with 400 + @app.put("/study_area/{session_id}", response_model=schemas.StudyArea) def update_study_area(session_id: str, study_area: schemas.StudyArea, @@ -242,8 +220,6 @@ def delete_scenario(scenario_id: int, db: Session = Depends(get_db)): return crud.delete_scenario(db=db, scenario_id=scenario_id) -### Worker Endpoints ### - @app.get("/jobsqueue/") async def worker_job_request(db: Session = Depends(get_db)): """If there's work to be done in the queue send it to the worker.""" @@ -315,14 +291,14 @@ def worker_invest_response( @app.post("/jobsqueue/scenario") def worker_scenario_response( - scenario_job: schemas.WorkerResponse, db: Session = Depends(get_db)): + scenario_job: schemas.WorkerResponse, db: Session = Depends(get_db)): """Update the db given the job details from the worker. Returned URL result will be partial to allow for local vs cloud stored depending on production vs dev environment. Args: - scenario_job (pydantic model): a pydantic model with the following + scenario_job (pydantic model): a pydantic model with the following key/vals "result": { @@ -373,7 +349,8 @@ def worker_scenario_response( @app.post("/jobsqueue/parcel_stats") def worker_parcel_stats_response( - parcel_stats_job: schemas.WorkerResponse, db: Session = Depends(get_db)): + parcel_stats_job: schemas.WorkerResponse, + db: Session = Depends(get_db)): """Update the db given the job details from the worker.""" LOGGER.debug("Entering jobsqueue/parcel_stats") LOGGER.debug(parcel_stats_job) @@ -408,14 +385,14 @@ def worker_parcel_stats_response( @app.post("/jobsqueue/pattern") def worker_pattern_response( - pattern_job: schemas.WorkerResponse, db: Session = Depends(get_db)): + pattern_job: schemas.WorkerResponse, db: Session = Depends(get_db)): """Update the db given the job details from the worker. Returned URL result will be partial to allow for local vs cloud stored depending on production vs dev environment. Args: - pattern_job (pydantic model): a pydantic model with the following + pattern_job (pydantic model): a pydantic model with the following key/vals { @@ -484,8 +461,6 @@ def read_jobs(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): return jobs -### Task Endpoints ### - @app.post("/lulc_codes/", response_model=schemas.JobResponse) def get_lulc_info(db: Session = Depends(get_db)): """Get the lulc class codes, names, and color representation.""" @@ -590,7 +565,7 @@ def wallpaper(wallpaper: schemas.Wallpaper, db: Session = Depends(get_db)): @app.post("/lulc_fill/", response_model=schemas.JobResponse) def lulc_fill(lulc_fill: schemas.ParcelFill, - db: Session = Depends(get_db)): + db: Session = Depends(get_db)): # Get Scenario details from scenario_id scenario_db = crud.get_scenario(db, lulc_fill.scenario_id) study_area_id = scenario_db.study_area_id @@ -613,7 +588,7 @@ def lulc_fill(lulc_fill: schemas.ParcelFill, }, "job_args": { "target_parcel_wkt": study_area_wkt, - "lulc_class": lulc_fill.lulc_class, #TODO: make sure this is a WKT string and no just a bounding box + "lulc_class": lulc_fill.lulc_class, "lulc_source_url": f'{WORKING_ENV}/{scenario_db.lulc_url_base}', } } @@ -808,25 +783,3 @@ def get_invest_results(scenario_id: int, db: Session = Depends(get_db)): 'results': invest_results, 'serviceshed': serviceshed } - - -### Testing ideas from tutorial ### - -client = TestClient(app) - - -def test_read_main(): - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"msg": "Hello World: prototype test"} - -def test_add_jobs(): - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"msg": "Hello World"} - - # read status of job - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"msg": "Hello World"} - From 576b1be959c4d38618e35487b4669dd2997c699f Mon Sep 17 00:00:00 2001 From: davemfish Date: Wed, 6 Sep 2023 11:18:48 -0400 Subject: [PATCH 02/37] import lulc table into DB if it does not already exist. --- README.md | 2 +- server/sql_app/main.py | 24 +++++++++++++++++++++--- server/sql_app/models.py | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f468a38..de1098a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ $ docker compose up --build These are data used by the python worker. local path which mounts in container - (bucket where source file can be found) -`appdata/lucodes_crosswalk.csv` +`appdata/lulc_crosswalk.csv` `appdata/NLCD_2016_epsg3857.tif` (natcap-urban-online-datasets-**public**) `appdata/invest-data/CGIAR_et0_annual_epsg_3857.tif` (natcap-urban-online-datasets) `appdata/invest-data/OE_Bioregions_3857.shp` (natcap-urban-online-datasets) diff --git a/server/sql_app/main.py b/server/sql_app/main.py index 8c4c07d..bf77d0e 100644 --- a/server/sql_app/main.py +++ b/server/sql_app/main.py @@ -1,3 +1,4 @@ +import csv import json import logging import os @@ -9,11 +10,11 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi import Depends, FastAPI, HTTPException, Request -from fastapi.testclient import TestClient from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY from sqlalchemy.orm import Session +from sqlalchemy import event from . import crud, models, schemas from .database import SessionLocal, engine @@ -30,6 +31,7 @@ # When gathering URL result for frontend request build the URL with this: WORKING_ENV = "/opt/appdata" BASE_LULC = "NLCD_2016_epsg3857.tif" +LULC_CSV_PATH = os.path.join(WORKING_ENV, 'lulc_crosswalk.csv') # Create a queue that we will use to store our "workload". QUEUE = queue.PriorityQueue() @@ -62,12 +64,28 @@ "stats_under_parcel": "stats_under_parcel", } -models.Base.metadata.create_all(bind=engine) + +def insert_lulc_data(target, connection, **kw): + LOGGER.info('importing LULC Crosswalk table') + # https://docs.sqlalchemy.org/en/20/_modules/examples/performance/bulk_inserts.html + with open(LULC_CSV_PATH, 'r') as file: + reader = csv.DictReader(file) + connection.execute( + models.LulcCrosswalk.__table__.insert(), + [row for row in reader] + ) + + +# create_all() will check if tables already exist before creating them. +# if this table did not exist, populate it from the CSV. +event.listen(models.LulcCrosswalk.__table__, 'after_create', insert_lulc_data) + # Normally you would probably initialize your db (create tables, etc) with # Alembic. Would also use Alembic for "migrations" (that's its main job). # A "migration" is the set of steps needed whenever you change the structure # of your SQLA models, add a new attribute, etc. to replicate those changes # in the db, add a new column, a new table, etc. +models.Base.metadata.create_all(bind=engine) app = FastAPI() origins = ["http://localhost:3000"] @@ -495,7 +513,7 @@ def create_pattern(session_id: str, pattern: schemas.PatternBase, }, "job_args": { "pattern_wkt": pattern_db.wkt, - "lulc_source_url": os.path.join(WORKING_ENV,BASE_LULC), + "lulc_source_url": os.path.join(WORKING_ENV, BASE_LULC), } } diff --git a/server/sql_app/models.py b/server/sql_app/models.py index 36d4513..47d623f 100644 --- a/server/sql_app/models.py +++ b/server/sql_app/models.py @@ -134,3 +134,23 @@ class InvestResult(Base): serviceshed = Column(String) #scenario = relationship("Scenario", back_populates="invest_results") + + +class LulcCrosswalk(Base): + """Lookup table for landuse-landcover codes and labels.""" + __tablename__ = "lulc_crosswalk" + + lucode = Column(Integer, primary_key=True) + code = Column(Integer) + nlud = Column(Integer) + nlud_tier_1 = Column(String) + nlud_tier_2 = Column(String) + nlud_tier_3 = Column(String) + nlud_colors = Column(String) + nlcd = Column(Integer) + nlcd_lulc = Column(String) + nlcd_colors = Column(String) + tree = Column(Integer) + tree_canopy_cover = Column(String) + tree_colors = Column(String) + From d69e3cc1d5148cfbfde8756a4326cfa498991830 Mon Sep 17 00:00:00 2001 From: davemfish Date: Thu, 7 Sep 2023 16:27:53 -0400 Subject: [PATCH 03/37] added requests to new endpoints; new dropdown component --- frontend/src/edit/lulcMenu.jsx | 69 +++++++++++++++++++++++++++ frontend/src/edit/scenarioBuilder.jsx | 1 + frontend/src/requests.js | 46 +++++++++++++++++- server/sql_app/crud.py | 31 ++++++++++++ server/sql_app/main.py | 32 ++++++++++++- server/sql_app/schemas.py | 1 - 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 frontend/src/edit/lulcMenu.jsx diff --git a/frontend/src/edit/lulcMenu.jsx b/frontend/src/edit/lulcMenu.jsx new file mode 100644 index 0000000..e811350 --- /dev/null +++ b/frontend/src/edit/lulcMenu.jsx @@ -0,0 +1,69 @@ +import React, { useEffect, useState } from 'react'; + +import { + getNLUDTier2, + getNLUDTier3, + getNLCD, +} from '../requests'; + +export default function LulcMenu(props) { + const [nlud2Options, setNlud2Options] = useState([]); + const [nlud3Options, setNlud3Options] = useState([]); + const [nlcdOptions, setNlcdOptions] = useState([]); + const [nlud2, setNlud2] = useState(null); + const [nlud3, setNlud3] = useState(null); + const [nlcd, setNlcd] = useState(null); + const [tree, setTree] = useState(null); + + useEffect(() => { + (async () => { + const options = await getNLUDTier2(); + setNlud2Options(options); + })(); + }, []); + + useEffect(() => { + (async () => { + const options = await getNLUDTier3(nlud2); + setNlud3Options(options); + })(); + }, [nlud2]); + + useEffect(() => { + (async () => { + const options = await getNLCD(nlud2, nlud3); + setNlcdOptions(options); + })(); + }, [nlud2, nlud3]); + + useEffect(() => { + if (nlud2 && nlud3 && nlcd && tree) { + props.setLucode(nlud2, nlud3, nlcd, tree); + } + }, [nlud2, nlud3, nlcd, tree]); + + return ( + <> + setNlud2(event.target.value)} + > + {nlud2Options.map((name) => )} + + setNlud3(event.target.value)} + > + {nlud3Options.map((name) => )} + + setNlcd(event.target.value)} + > + {nlcdOptions.map((name) => )} + + setTree(event.target.value)} + > + {['none', 'low', 'medium'].map((name) => )} + + + ); +} diff --git a/frontend/src/edit/scenarioBuilder.jsx b/frontend/src/edit/scenarioBuilder.jsx index e618daa..9857842 100644 --- a/frontend/src/edit/scenarioBuilder.jsx +++ b/frontend/src/edit/scenarioBuilder.jsx @@ -38,6 +38,7 @@ export default function ScenarioBuilder(props) { const [scenarioID, setScenarioID] = useState(null); const [selectedPattern, setSelectedPattern] = useState(null); const [jobID, setJobID] = useState(null); + const [nlud2Options, setNLUD2Options] = useState([]); useInterval(async () => { // There are sometimes two jobs submitted concurrently. diff --git a/frontend/src/requests.js b/frontend/src/requests.js index b8b7632..28f3601 100644 --- a/frontend/src/requests.js +++ b/frontend/src/requests.js @@ -331,4 +331,48 @@ export async function getInvestResults(scenarioID) { .then((response) => response.json()) .catch((error) => console.log(error)) ); -} \ No newline at end of file +} + +export async function getNLUDTier2() { + return ( + window.fetch(`${apiBaseURL}/lucodes/nlude_tier_2`, { + method: 'get', + headers: { 'Content-Type': 'application/json' }, + }) + .then((response) => response.json()) + .catch((error) => console.log(error)) + ); +} + +export async function getNLUDTier3(tier2) { + return ( + window.fetch(`${apiBaseURL}/lucodes/nlude_tier_3/${tier2}`, { + method: 'get', + headers: { 'Content-Type': 'application/json' }, + }) + .then((response) => response.json()) + .catch((error) => console.log(error)) + ); +} + +export async function getNLCD(tier2, tier3) { + return ( + window.fetch(`${apiBaseURL}/lucodes/nlcd/${tier2}/${tier3}`, { + method: 'get', + headers: { 'Content-Type': 'application/json' }, + }) + .then((response) => response.json()) + .catch((error) => console.log(error)) + ); +} + +export async function getLucode(tier2, tier3, nlcd, tree) { + return ( + window.fetch(`${apiBaseURL}/lucodes/lucode/${tier2}/${tier3}/${nlcd}/${tree}`, { + method: 'get', + headers: { 'Content-Type': 'application/json' }, + }) + .then((response) => response.json()) + .catch((error) => console.log(error)) + ); +} diff --git a/server/sql_app/crud.py b/server/sql_app/crud.py index 45d9930..9e04581 100644 --- a/server/sql_app/crud.py +++ b/server/sql_app/crud.py @@ -320,3 +320,34 @@ def update_invest(db: Session, scenario_id: int, job_id: int, db.commit() db.refresh(db_invest) return STATUS_SUCCESS + + +def get_nlud_tier_2(db: Session): + data = db.query(models.LulcCrosswalk.nlud_tier_2).distinct() + LOGGER.info(data) + return data + + +def get_nlud_tier_3(db: Session, nlud_tier_2: str): + return db.query( + models.LulcCrosswalk.nlud_tier_3).filter( + models.LulcCrosswalk.nlud_tier_2 == nlud_tier_2).distinct() + + +def get_nlcd(db: Session, nlud_tier_2: str, nlud_tier_3: str): + return db.query( + models.LulcCrosswalk.nlcd_lulc).filter( + models.LulcCrosswalk.nlud_tier_2 == nlud_tier_2, + models.LulcCrosswalk.nlud_tier_3 == nlud_tier_3).distinct() + + +# TODO: this set of queries is not optimal because LulcCrosswalk is +# not a normalized database. We could query more efficiently if it was +# normalized into multiple tables. +def get_lucode(db: Session, nlud_tier_2: str, nlud_tier_3: str, nlcd: str, tree: str): + return db.query( + models.LulcCrosswalk.lucode).filter( + models.LulcCrosswalk.nlud_tier_2 == nlud_tier_2, + models.LulcCrosswalk.nlud_tier_3 == nlud_tier_3, + models.LulcCrosswalk.nlcd_lulc == nlcd, + models.LulcCrosswalk.tree_canopy_cover == tree).distinct() diff --git a/server/sql_app/main.py b/server/sql_app/main.py index bf77d0e..9a86b9a 100644 --- a/server/sql_app/main.py +++ b/server/sql_app/main.py @@ -176,8 +176,7 @@ def get_study_area( return db_study_area -@app.put("/study_area/{session_id}", - response_model=schemas.StudyArea) +@app.put("/study_area/{session_id}", response_model=schemas.StudyArea) def update_study_area(session_id: str, study_area: schemas.StudyArea, db: Session = Depends(get_db)): # check that the session exists @@ -801,3 +800,32 @@ def get_invest_results(scenario_id: int, db: Session = Depends(get_db)): 'results': invest_results, 'serviceshed': serviceshed } + + +@app.get("/lucodes/nlud_tier_2") +def get_nlud_tier_2(db: Session = Depends(get_db)) -> list[str]: + db_list = crud.get_nlud_tier_2(db) + return [row.nlud_tier_2 for row in db_list] + + +@app.get("/lucodes/nlud_tier_3/{nlud_tier_2}") +def get_nlud_tier_3(nlud_tier_2: str, db: Session = Depends(get_db)) -> list[str]: + db_list = crud.get_nlud_tier_3(db, nlud_tier_2=nlud_tier_2) + return [row.nlud_tier_3 for row in db_list] + + +@app.get("/lucodes/nlcd/{nlud_tier_2}/{nlud_tier_3}") +def get_nlcd(nlud_tier_2: str, nlud_tier_3: str, + db: Session = Depends(get_db)) -> list[str]: + db_list = crud.get_nlcd(db, nlud_tier_2=nlud_tier_2, nlud_tier_3=nlud_tier_3) + return [row.nlcd_lulc for row in db_list] + + +# TODO: will all 3 tree cover classes always be present for each category? +# or do we need another query to find out which are present? +@app.get("/lucodes/lucode/{nlud_tier_2}/{nlud_tier_3}/{nlcd}/{tree}") +def get_lucode(nlud_tier_2: str, nlud_tier_3: str, nlcd: str, tree: str, + db: Session = Depends(get_db)) -> int: + db_list = crud.get_lucode( + db, nlud_tier_2=nlud_tier_2, nlud_tier_3=nlud_tier_3, nlcd=nlcd, tree=tree) + return [row.lucode for row in db_list] diff --git a/server/sql_app/schemas.py b/server/sql_app/schemas.py index 60a2b57..be72a78 100644 --- a/server/sql_app/schemas.py +++ b/server/sql_app/schemas.py @@ -221,4 +221,3 @@ class ParcelFill(BaseModel): class Config: orm_mode = True - From 119d4216d6227921d86f23dfb3f28bf1dd75618d Mon Sep 17 00:00:00 2001 From: davemfish Date: Fri, 8 Sep 2023 09:57:46 -0400 Subject: [PATCH 04/37] pin openlayers version --- frontend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 405fc4f..4334401 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,7 @@ "dotenv": "^16.0.0", "jsts": "^2.9.3", "localforage": "^1.10.0", - "ol": "latest", + "ol": "^6.15.1", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -38,7 +38,7 @@ "happy-dom": "^9.20.3", "jsdom": "^22.1.0", "puppeteer": "^20.7.4", - "vite": "^4.3.9", + "vite": "^4.4.9", "vitest": "^0.33.0" } } From 05426422887b989b92791796f80b36415f5fc16e Mon Sep 17 00:00:00 2001 From: davemfish Date: Mon, 11 Sep 2023 11:29:55 -0400 Subject: [PATCH 05/37] pin openlayers version; lockfile --- frontend/yarn.lock | 167 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 6a467e5..12d9e0b 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -324,111 +324,221 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== +"@esbuild/android-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" + integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== + "@esbuild/android-arm@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== +"@esbuild/android-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" + integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== + "@esbuild/android-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== +"@esbuild/android-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" + integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== + "@esbuild/darwin-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== +"@esbuild/darwin-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" + integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== + "@esbuild/darwin-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== +"@esbuild/darwin-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" + integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== + "@esbuild/freebsd-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== +"@esbuild/freebsd-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" + integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== + "@esbuild/freebsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== +"@esbuild/freebsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" + integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== + "@esbuild/linux-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== +"@esbuild/linux-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" + integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== + "@esbuild/linux-arm@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== +"@esbuild/linux-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" + integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== + "@esbuild/linux-ia32@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== +"@esbuild/linux-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" + integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== + "@esbuild/linux-loong64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== +"@esbuild/linux-loong64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" + integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== + "@esbuild/linux-mips64el@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== +"@esbuild/linux-mips64el@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" + integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== + "@esbuild/linux-ppc64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== +"@esbuild/linux-ppc64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" + integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== + "@esbuild/linux-riscv64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== +"@esbuild/linux-riscv64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" + integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== + "@esbuild/linux-s390x@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== +"@esbuild/linux-s390x@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" + integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== + "@esbuild/linux-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== +"@esbuild/linux-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== + "@esbuild/netbsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== +"@esbuild/netbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" + integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== + "@esbuild/openbsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== +"@esbuild/openbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" + integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== + "@esbuild/sunos-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== +"@esbuild/sunos-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" + integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== + "@esbuild/win32-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== +"@esbuild/win32-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" + integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== + "@esbuild/win32-ia32@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== +"@esbuild/win32-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" + integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== + "@esbuild/win32-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== +"@esbuild/win32-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" + integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== + "@eslint/eslintrc@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" @@ -1681,6 +1791,34 @@ esbuild@^0.17.5: "@esbuild/win32-ia32" "0.17.19" "@esbuild/win32-x64" "0.17.19" +esbuild@^0.18.10: + version "0.18.20" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" + integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== + optionalDependencies: + "@esbuild/android-arm" "0.18.20" + "@esbuild/android-arm64" "0.18.20" + "@esbuild/android-x64" "0.18.20" + "@esbuild/darwin-arm64" "0.18.20" + "@esbuild/darwin-x64" "0.18.20" + "@esbuild/freebsd-arm64" "0.18.20" + "@esbuild/freebsd-x64" "0.18.20" + "@esbuild/linux-arm" "0.18.20" + "@esbuild/linux-arm64" "0.18.20" + "@esbuild/linux-ia32" "0.18.20" + "@esbuild/linux-loong64" "0.18.20" + "@esbuild/linux-mips64el" "0.18.20" + "@esbuild/linux-ppc64" "0.18.20" + "@esbuild/linux-riscv64" "0.18.20" + "@esbuild/linux-s390x" "0.18.20" + "@esbuild/linux-x64" "0.18.20" + "@esbuild/netbsd-x64" "0.18.20" + "@esbuild/openbsd-x64" "0.18.20" + "@esbuild/sunos-x64" "0.18.20" + "@esbuild/win32-arm64" "0.18.20" + "@esbuild/win32-ia32" "0.18.20" + "@esbuild/win32-x64" "0.18.20" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -3400,6 +3538,15 @@ postcss@^8.4.23: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.27: + version "8.4.29" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" + integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -3689,6 +3836,13 @@ rollup@^3.21.0: optionalDependencies: fsevents "~2.3.2" +rollup@^3.27.1: + version "3.29.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.0.tgz#1b40e64818afc979c7e5bef93de675829288986b" + integrity sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w== + optionalDependencies: + fsevents "~2.3.2" + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -4229,7 +4383,7 @@ vite-node@0.33.0: picocolors "^1.0.0" vite "^3.0.0 || ^4.0.0" -"vite@^3.0.0 || ^4.0.0", vite@^4.3.9: +"vite@^3.0.0 || ^4.0.0": version "4.3.9" resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d" integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg== @@ -4240,6 +4394,17 @@ vite-node@0.33.0: optionalDependencies: fsevents "~2.3.2" +vite@^4.4.9: + version "4.4.9" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d" + integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA== + dependencies: + esbuild "^0.18.10" + postcss "^8.4.27" + rollup "^3.27.1" + optionalDependencies: + fsevents "~2.3.2" + vitest@^0.33.0: version "0.33.0" resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.33.0.tgz#e2be6153aec1d30e3460ac6d64265bf72da2551c" From 69580451ca0e51b6502cbdaaa5dd1b4d04bf1375 Mon Sep 17 00:00:00 2001 From: davemfish Date: Mon, 11 Sep 2023 11:30:42 -0400 Subject: [PATCH 06/37] integrating and debugging new dropdowns --- frontend/src/edit/lulcMenu.jsx | 68 +++++++++++++++++---------- frontend/src/edit/scenarioBuilder.jsx | 19 ++++---- frontend/src/requests.js | 19 ++++++-- server/sql_app/crud.py | 2 +- server/sql_app/main.py | 18 ++++--- server/sql_app/schemas.py | 7 +++ 6 files changed, 84 insertions(+), 49 deletions(-) diff --git a/frontend/src/edit/lulcMenu.jsx b/frontend/src/edit/lulcMenu.jsx index e811350..20ddfc7 100644 --- a/frontend/src/edit/lulcMenu.jsx +++ b/frontend/src/edit/lulcMenu.jsx @@ -1,11 +1,18 @@ import React, { useEffect, useState } from 'react'; +import { + HTMLSelect, +} from '@blueprintjs/core'; + import { getNLUDTier2, getNLUDTier3, getNLCD, + getLucode, } from '../requests'; +const treeOptions = ['none', 'low', 'medium'] + export default function LulcMenu(props) { const [nlud2Options, setNlud2Options] = useState([]); const [nlud3Options, setNlud3Options] = useState([]); @@ -13,12 +20,13 @@ export default function LulcMenu(props) { const [nlud2, setNlud2] = useState(null); const [nlud3, setNlud3] = useState(null); const [nlcd, setNlcd] = useState(null); - const [tree, setTree] = useState(null); + const [tree, setTree] = useState(treeOptions[0]); useEffect(() => { (async () => { const options = await getNLUDTier2(); setNlud2Options(options); + setNlud2(options[0]); })(); }, []); @@ -26,6 +34,7 @@ export default function LulcMenu(props) { (async () => { const options = await getNLUDTier3(nlud2); setNlud3Options(options); + setNlud3(options[0]); })(); }, [nlud2]); @@ -33,37 +42,44 @@ export default function LulcMenu(props) { (async () => { const options = await getNLCD(nlud2, nlud3); setNlcdOptions(options); + setNlcd(options[0]); })(); }, [nlud2, nlud3]); useEffect(() => { if (nlud2 && nlud3 && nlcd && tree) { - props.setLucode(nlud2, nlud3, nlcd, tree); + (async () => { + const code = await getLucode(nlud2, nlud3, nlcd, tree); + console.log(code) + props.setLucode(code); + })(); } }, [nlud2, nlud3, nlcd, tree]); - return ( - <> - setNlud2(event.target.value)} - > - {nlud2Options.map((name) => )} - - setNlud3(event.target.value)} - > - {nlud3Options.map((name) => )} - - setNlcd(event.target.value)} - > - {nlcdOptions.map((name) => )} - - setTree(event.target.value)} - > - {['none', 'low', 'medium'].map((name) => )} - - - ); + if (nlud2Options) { + return ( + <> + setNlud2(event.target.value)} + > + {nlud2Options.map((name) => )} + + setNlud3(event.target.value)} + > + {nlud3Options.map((name) => )} + + setNlcd(event.target.value)} + > + {nlcdOptions.map((name) => )} + + setTree(event.target.value)} + > + {treeOptions.map((name) => )} + + + ); + } } diff --git a/frontend/src/edit/scenarioBuilder.jsx b/frontend/src/edit/scenarioBuilder.jsx index 9857842..eadae9f 100644 --- a/frontend/src/edit/scenarioBuilder.jsx +++ b/frontend/src/edit/scenarioBuilder.jsx @@ -3,15 +3,16 @@ import React, { useState } from 'react'; import { Button, InputGroup, - HTMLSelect, + // HTMLSelect, Radio, RadioGroup, Spinner } from '@blueprintjs/core'; import useInterval from '../hooks/useInterval'; -import landuseCodes from '../../../appdata/NLCD_2016.lulcdata.json'; +// import landuseCodes from '../../../appdata/NLCD_2016.lulcdata.json'; import WallpaperingMenu from './wallpaperingMenu'; +import LulcMenu from './lulcMenu'; import { createScenario, getJobStatus, @@ -32,13 +33,12 @@ export default function ScenarioBuilder(props) { scenarioNames, } = props; - const [singleLULC, setSingleLULC] = useState(Object.keys(landuseCodes)[0]); + const [singleLULC, setSingleLULC] = useState(null); const [conversionOption, setConversionOption] = useState('fill'); const [scenarioName, setScenarioName] = useState(''); const [scenarioID, setScenarioID] = useState(null); const [selectedPattern, setSelectedPattern] = useState(null); const [jobID, setJobID] = useState(null); - const [nlud2Options, setNLUD2Options] = useState([]); useInterval(async () => { // There are sometimes two jobs submitted concurrently. @@ -87,7 +87,7 @@ export default function ScenarioBuilder(props) { if (conversionOption === 'fill' && singleLULC) { scenarioDescription = ( - Create a scenario by filling with {landuseCodes[singleLULC].name} + Create a scenario by filling with {singleLULC} ); } @@ -110,12 +110,9 @@ export default function ScenarioBuilder(props) { { (conversionOption === 'fill') ? ( - setSingleLULC(event.target.value)} - > - {Object.entries(landuseCodes) - .map(([code, data]) => )} - + ) : ( response.json()) .catch((error) => console.log(error)) diff --git a/server/sql_app/crud.py b/server/sql_app/crud.py index 9e04581..51e19de 100644 --- a/server/sql_app/crud.py +++ b/server/sql_app/crud.py @@ -350,4 +350,4 @@ def get_lucode(db: Session, nlud_tier_2: str, nlud_tier_3: str, nlcd: str, tree: models.LulcCrosswalk.nlud_tier_2 == nlud_tier_2, models.LulcCrosswalk.nlud_tier_3 == nlud_tier_3, models.LulcCrosswalk.nlcd_lulc == nlcd, - models.LulcCrosswalk.tree_canopy_cover == tree).distinct() + models.LulcCrosswalk.tree_canopy_cover == tree).first() diff --git a/server/sql_app/main.py b/server/sql_app/main.py index 9a86b9a..a5aa4fd 100644 --- a/server/sql_app/main.py +++ b/server/sql_app/main.py @@ -4,6 +4,7 @@ import os import queue import sys +from typing import Optional import shapely.geometry import shapely.wkt @@ -823,9 +824,14 @@ def get_nlcd(nlud_tier_2: str, nlud_tier_3: str, # TODO: will all 3 tree cover classes always be present for each category? # or do we need another query to find out which are present? -@app.get("/lucodes/lucode/{nlud_tier_2}/{nlud_tier_3}/{nlcd}/{tree}") -def get_lucode(nlud_tier_2: str, nlud_tier_3: str, nlcd: str, tree: str, - db: Session = Depends(get_db)) -> int: - db_list = crud.get_lucode( - db, nlud_tier_2=nlud_tier_2, nlud_tier_3=nlud_tier_3, nlcd=nlcd, tree=tree) - return [row.lucode for row in db_list] +@app.post("/lucodes/lucode") +def get_lucode(lulc_dict: schemas.LulcRequest, db: Session = Depends(get_db)) -> Optional[int]: + row = crud.get_lucode( + db, + nlud_tier_2=lulc_dict.nlud_tier_2, + nlud_tier_3=lulc_dict.nlud_tier_3, + nlcd=lulc_dict.nlcd, + tree=lulc_dict.tree) + if row: + return row.lucode + return None diff --git a/server/sql_app/schemas.py b/server/sql_app/schemas.py index be72a78..65555cf 100644 --- a/server/sql_app/schemas.py +++ b/server/sql_app/schemas.py @@ -221,3 +221,10 @@ class ParcelFill(BaseModel): class Config: orm_mode = True + + +class LulcRequest(BaseModel): + nlud_tier_2: str + nlud_tier_3: str + nlcd: str + tree: str From 04277f51959c2da0b4c20513cbca04bef9850636 Mon Sep 17 00:00:00 2001 From: davemfish Date: Mon, 11 Sep 2023 17:05:12 -0400 Subject: [PATCH 07/37] some layout for the dropdowns --- frontend/src/edit/lulcMenu.jsx | 60 ++++++++++++++++++++-------------- frontend/src/index.css | 19 +++++++++++ frontend/src/requests.js | 2 +- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/frontend/src/edit/lulcMenu.jsx b/frontend/src/edit/lulcMenu.jsx index 20ddfc7..65697fc 100644 --- a/frontend/src/edit/lulcMenu.jsx +++ b/frontend/src/edit/lulcMenu.jsx @@ -11,7 +11,7 @@ import { getLucode, } from '../requests'; -const treeOptions = ['none', 'low', 'medium'] +const treeOptions = ['none', 'low', 'medium']; export default function LulcMenu(props) { const [nlud2Options, setNlud2Options] = useState([]); @@ -50,7 +50,7 @@ export default function LulcMenu(props) { if (nlud2 && nlud3 && nlcd && tree) { (async () => { const code = await getLucode(nlud2, nlud3, nlcd, tree); - console.log(code) + console.log(code); props.setLucode(code); })(); } @@ -58,28 +58,40 @@ export default function LulcMenu(props) { if (nlud2Options) { return ( - <> - setNlud2(event.target.value)} - > - {nlud2Options.map((name) => )} - - setNlud3(event.target.value)} - > - {nlud3Options.map((name) => )} - - setNlcd(event.target.value)} - > - {nlcdOptions.map((name) => )} - - setTree(event.target.value)} - > - {treeOptions.map((name) => )} - - +
+ + + +
); } } diff --git a/frontend/src/index.css b/frontend/src/index.css index a183452..e11c2d1 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -122,6 +122,25 @@ html, body, #root, .App { padding-top: 0.5rem; } +#landuse-select-form { + display: flex; + flex-direction: column; +} + +.lulc-select { + display: flex; + justify-content: space-between; + flex-wrap: nowrap; + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.lulc-select .bp4-html-select { + flex-grow: 1; + margin-left: 1rem; + margin-right: 1rem; +} + .wallpaper-options { display: inline-block; vertical-align: top; diff --git a/frontend/src/requests.js b/frontend/src/requests.js index 8ac2b5d..f4c638b 100644 --- a/frontend/src/requests.js +++ b/frontend/src/requests.js @@ -345,7 +345,7 @@ export async function getNLUDTier2() { } export async function getNLUDTier3(tier2) { - const t2 = encodeURIComponent(t23); + const t2 = encodeURIComponent(tier2); return ( window.fetch(`${apiBaseURL}/lucodes/nlud_tier_3/${t2}`, { method: 'get', From 8533db1d30d59db3801a2c5b144a6cb6a732e589 Mon Sep 17 00:00:00 2001 From: davemfish Date: Tue, 12 Sep 2023 10:25:04 -0400 Subject: [PATCH 08/37] more layout and debugging requests. --- frontend/src/edit/lulcMenu.jsx | 26 +++++++++++++++----------- frontend/src/index.css | 15 +++++++++++---- frontend/src/requests.js | 19 ++++++++++++------- server/sql_app/main.py | 21 ++++++++++++++------- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/frontend/src/edit/lulcMenu.jsx b/frontend/src/edit/lulcMenu.jsx index 65697fc..9b23905 100644 --- a/frontend/src/edit/lulcMenu.jsx +++ b/frontend/src/edit/lulcMenu.jsx @@ -32,17 +32,21 @@ export default function LulcMenu(props) { useEffect(() => { (async () => { - const options = await getNLUDTier3(nlud2); - setNlud3Options(options); - setNlud3(options[0]); + if (nlud2) { + const options = await getNLUDTier3(nlud2); + setNlud3Options(options); + setNlud3(options[0]); + } })(); }, [nlud2]); useEffect(() => { (async () => { - const options = await getNLCD(nlud2, nlud3); - setNlcdOptions(options); - setNlcd(options[0]); + if (nlud2 && nlud3) { + const options = await getNLCD(nlud2, nlud3); + setNlcdOptions(options); + setNlcd(options[0]); + } })(); }, [nlud2, nlud3]); @@ -60,23 +64,23 @@ export default function LulcMenu(props) { return (