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

Tracking release/workbench-alpha branch to support the workbench #399

Merged
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
986f6ac
I. #319 Fixing Windows model links.
dcdenu4 Oct 15, 2020
7ed9ad7
update sample data and test date rev
dcdenu4 Nov 16, 2020
878ba36
Merge branch 'release/3.9' into task/release-39-update-repo-versions
dcdenu4 Nov 16, 2020
f1b00a7
update sample-data rev for UCM json
dcdenu4 Nov 16, 2020
bb0fa57
Adding syslog logging to launcher. RE:#384
phargogh Nov 17, 2020
c20f4b1
Adding environment variable for Qt launch on Big Sur. RE:#384
phargogh Nov 17, 2020
407864e
Correcting environment variable value type. RE:#384
phargogh Nov 17, 2020
7cc9361
Adding stdout/stderr to the launcher subprocess, hoping to get more f…
phargogh Nov 17, 2020
ad9d01d
update deprecated LOGGER.warn, open U option, assertEquals
emlys Nov 17, 2020
16173d7
Noting change in history. RE:#384
phargogh Nov 18, 2020
9ea1ff9
Only setting the QT_MAC_WANTS_LAYER variable on mac. RE:#384
phargogh Nov 18, 2020
6910bf1
Bumping up the sleep length to coax a test into passing. RE:#384
phargogh Nov 18, 2020
2576c43
more deprecated
emlys Nov 18, 2020
47c467f
fix recreation dtype metadata warning
emlys Nov 18, 2020
e7daad5
replace numpy matrix with array in fisheries
emlys Nov 18, 2020
cfd67dd
cleanup
emlys Nov 18, 2020
cb9a443
fix ui test warnings
emlys Nov 18, 2020
67196f8
Update makefile for creating ENV
dcdenu4 Nov 19, 2020
3b83021
fix history indenting
dcdenu4 Nov 19, 2020
4981bbf
update UG rev to latest 3.9 version
dcdenu4 Nov 19, 2020
8b5b5ec
added flask app module and a cli entry-point for it
davemfish Nov 19, 2020
ba1fea8
deploy the entire dist dir so invest executeable is available for dow…
davemfish Nov 19, 2020
220bc96
remove #pip-only tags from requirements available on conda-forge
emlys Nov 19, 2020
7714119
don't use cache, install nomkl before scipy
emlys Nov 19, 2020
ab5283f
undo some changes to identify the conflict
emlys Nov 19, 2020
83508ef
move gui requirements to conda
emlys Nov 19, 2020
fd7b4b0
remove #PIP-ONLY from qtawesome
emlys Nov 19, 2020
4ce1e02
remove extra steps of conda install
emlys Nov 19, 2020
8f6a149
Reverting changes made to the launcher. The benefits of the changes …
phargogh Nov 20, 2020
dfc2359
zip the binaries before deploying
davemfish Nov 20, 2020
c843c22
deploy step needs to skip on nonzero exit.
davemfish Nov 20, 2020
930a713
Merge branch 'release/3.9' into task/release-39-update-repo-versions
dcdenu4 Nov 20, 2020
c084f83
Attempting to run the mac build on Big Sur to see if it avoids hdiuti…
phargogh Nov 20, 2020
1feb305
fixing the zip binaries command
davemfish Nov 21, 2020
b52992a
fixing the zip binaries command
davemfish Nov 21, 2020
d978181
make PyInstaller script compatible with ubuntu
davemfish Nov 23, 2020
0f8b7f6
giving up on updating requirements to conda for now
emlys Nov 23, 2020
d73234e
Reverting back to OS 10.15, specifying DMG compression, adding a lice…
phargogh Nov 23, 2020
89c9c56
Merge branch 'release/3.9' of https://github.com/natcap/invest into b…
phargogh Nov 23, 2020
9e31b4b
Updating mac build to include the HTML docs within the .app directory…
phargogh Nov 23, 2020
1f8b862
Merge branch 'bugfix/userguide-documentation-link' of https://github.…
phargogh Nov 23, 2020
9bae7a2
Merge branch 'bugfix/384-dev-build-hangs-on-big-sur' into bugfix/319-…
phargogh Nov 23, 2020
5fb84a8
Correting how we copy directories around. RE:#319
phargogh Nov 23, 2020
77313c8
Noting corrected local documentation links on mac. RE:#319
phargogh Nov 23, 2020
b166af5
added a cli test for the new serve entry-point
davemfish Nov 28, 2020
939542e
renamed the flask app module
davemfish Nov 28, 2020
2cce535
Merge branch 'release/3.9' into bugfix/make-env-windows
dcdenu4 Nov 30, 2020
5873e18
removing custom hooks that conflict with built-in pyinstaller hooks
davemfish Nov 30, 2020
8fd92c4
Revert "removing custom hooks that conflict with built-in pyinstaller…
davemfish Nov 30, 2020
289301e
revert the changes to pyinstaller script that were trying to make it …
davemfish Nov 30, 2020
ee99530
missed a module rename
davemfish Nov 30, 2020
6cf217c
a little linting
davemfish Nov 30, 2020
e96ed0c
Adding default language to the DMG. RE:#384
phargogh Nov 30, 2020
d849a07
Clarifying CWD in dmgconf script. RE:#384
phargogh Nov 30, 2020
da8296a
Clarifying variable name and restoring the former directory location.…
phargogh Dec 1, 2020
f7f86a8
Merge branch 'bugfix/384-dev-build-hangs-on-big-sur' into bugfix/319-…
phargogh Dec 1, 2020
b752c9e
Merge branch 'release/3.9' into task/release-39-update-repo-versions
dcdenu4 Dec 3, 2020
e0729b6
Merge branch 'bugfix/make-env-windows' into task/release-39-update-re…
dcdenu4 Dec 3, 2020
e2ebff4
Update UG to latest with all of main updates
dcdenu4 Dec 3, 2020
16534c6
Merge pull request #392 from phargogh/bugfix/384-dev-build-hangs-on-b…
emlys Dec 3, 2020
5766656
Merge branch 'release/3.9' into bugfix/319-local-docs-link-broken
phargogh Dec 3, 2020
5fc0829
Merge pull request #396 from phargogh/bugfix/319-local-docs-link-broken
emlys Dec 4, 2020
9169a05
keep 'STOP' handling out of _numpy_loads function
emlys Dec 4, 2020
b70cb7c
update rec test
emlys Dec 4, 2020
a3f79e1
Merge pull request #400 from dcdenu4/task/release-39-update-repo-vers…
emlys Dec 4, 2020
f0eeb8b
masking nodata values needs to use a numpy.isclose equality check
davemfish Dec 7, 2020
76fe7be
pin gdal to 3.1.2
emlys Dec 7, 2020
d73bfc4
I. #406 Refactor and patch msa bugs.
dcdenu4 Dec 8, 2020
1360a94
Merge pull request #405 from davemfish/bugfix/CV-402-floating-point-n…
dcdenu4 Dec 8, 2020
63cf7f8
updating sample-data rev for roads
dcdenu4 Dec 8, 2020
766ec78
avoid solving model functions on fake data
emlys Dec 8, 2020
b3d496e
add history note
emlys Dec 8, 2020
6fbd9ab
cleanup
emlys Dec 8, 2020
4254b7a
fill biomass array with zeros'
emlys Dec 8, 2020
5480e8c
Merge pull request #407 from dcdenu4/bugfix/402-GLOBIO-sample-data-ou…
dcdenu4 Dec 9, 2020
0d52084
remove numerical prefixes from some start menu names so that they do …
davemfish Dec 9, 2020
1988fc1
adding numbers in CBC start menu links to indicate preprocessor is st…
davemfish Dec 9, 2020
fb9e8c0
changed logging level to debug for validation.timeout thread messages…
davemfish Dec 9, 2020
b71ef69
Merge pull request #411 from davemfish/task/LOGGING-402-validation-ti…
emlys Dec 9, 2020
b367681
Merge pull request #391 from emlys/task/255
richpsharp Dec 9, 2020
35096e5
specify biomass array type; fix comment spacing
emlys Dec 9, 2020
f3e3486
fixing reference to an incorrect filename for carbon-edge model docs.
davemfish Dec 9, 2020
2e6563a
note for HISTORY - also re-arranged subsections to keep General at th…
davemfish Dec 9, 2020
3c17323
Update HQ default half sat for sample data
dcdenu4 Dec 9, 2020
d161b3f
clarify what masking op does
emlys Dec 9, 2020
b0b9ecb
Merge pull request #410 from davemfish/task/WINDOWS-402-ordering-star…
dcdenu4 Dec 9, 2020
5fb0664
Merge remote-tracking branch 'upstream/release/3.9' into bugfix/DOCS-…
davemfish Dec 9, 2020
3898c4c
note in HISTORY for fixing the link
davemfish Dec 9, 2020
3223372
update history with correct value changes
dcdenu4 Dec 9, 2020
4175906
use numpy.zeros not numpy.empty
emlys Dec 9, 2020
5e01661
Merge pull request #415 from dcdenu4/task/habitat-quality-half-sat-up…
davemfish Dec 9, 2020
bf91c7d
still learning the alphabet
davemfish Dec 9, 2020
1dda818
Merge branch 'release/3.9' into bug/forest-carbon-overflow-error
richpsharp Dec 10, 2020
cb78a28
mask out edge distance nodata areas
emlys Dec 10, 2020
07d8c8e
Merge pull request #416 from davemfish/bugfix/DOCS-402-local-docs-not…
dcdenu4 Dec 10, 2020
54b15ca
Merge branch 'release/3.9' into bug/forest-carbon-overflow-error
emlys Dec 10, 2020
e9d469e
Merge pull request #408 from emlys/bug/forest-carbon-overflow-error
emlys Dec 10, 2020
b715e5e
refactor edge distance masking; use consistent nodata value
emlys Dec 10, 2020
79ba20e
added optional port subargument to the invest serve entry-point
davemfish Dec 10, 2020
4b2fe46
update tests and test data
emlys Dec 10, 2020
b5f2390
Merge branch 'release/3.9' into bug/414
emlys Dec 10, 2020
8ff89c7
use positive nodata value for byte type
emlys Dec 10, 2020
92a6f87
Merge branch 'bug/414' of https://github.com/emlys/invest into bug/414
emlys Dec 10, 2020
c04c32f
add history note
emlys Dec 10, 2020
4e4be05
Merge pull request #404 from emlys/task/390
dcdenu4 Dec 10, 2020
46c3374
update nodata value in comments
emlys Dec 11, 2020
249107b
Merge pull request #417 from emlys/bug/414
dcdenu4 Dec 11, 2020
c07a13a
I. #402 Updating History Makefile for release
dcdenu4 Dec 11, 2020
200079b
I. #402 fix FCEE UG link and remove GDAL pin
dcdenu4 Dec 11, 2020
b32524c
I. #402 updating TG to 0.10.1
dcdenu4 Dec 11, 2020
228106b
I. #402 bumping TG to 0.10.2
dcdenu4 Dec 11, 2020
548dcd2
Adding a missing dep task in sdr
dcdenu4 Dec 11, 2020
428f981
update history for sdr dep fix
dcdenu4 Dec 11, 2020
a23dd2b
Merge pull request #422 from dcdenu4/release/release-39
dcdenu4 Dec 12, 2020
e2a02a0
Merge pull request #424 from natcap/release/3.9
richpsharp Dec 12, 2020
17d40ce
renamed the flask app again, to ui_server.py
davemfish Jan 4, 2021
f1831d8
Merge remote-tracking branch 'upstream/main' into release/workbench-a…
davemfish Jan 4, 2021
1ce9caf
restricting pandas version until we fix compatibility in our main bra…
davemfish Jan 4, 2021
48a407f
noted new CLI entry-point in history
davemfish Jan 5, 2021
025f068
restricting rtree to a version that should be compatible with pyinsta…
davemfish Jan 5, 2021
2af300d
add logger name to logging format
emlys Jan 5, 2021
a54efdf
add history note
emlys Jan 5, 2021
75a8c12
Merge pull request #430 from emlys/bug/15
emlys Jan 6, 2021
74b90da
remove fix for old actions bug
emlys Jan 7, 2021
e55a5dc
add newline after label
emlys Jan 7, 2021
737b7b2
Merge pull request #435 from emlys/task/fix-mac-builds
davemfish Jan 7, 2021
dd914a7
merged upstream main and resolved history conflict
davemfish Jan 8, 2021
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ifeq ($(OS),Windows_NT)
.DEFAULT_GOAL := windows_installer
RM_DATA_DIR := $(RMDIR) $(DATA_DIR)
/ := '\'
OSNAME = 'windows'
else
NULL := /dev/null
PROGRAM_CHECK_SCRIPT := ./scripts/check_required_programs.sh
Expand All @@ -56,6 +57,7 @@ else

ifeq ($(shell sh -c 'uname -s 2>/dev/null || echo not'),Darwin) # mac OSX
.DEFAULT_GOAL := mac_dmg
OSNAME = 'mac'
else
.DEFAULT_GOAL := binaries
endif
Expand Down Expand Up @@ -110,6 +112,7 @@ TEST_DATAVALIDATOR := $(PYTHON) -m pytest -vs scripts/invest-autovalidate.py

# Target names.
INVEST_BINARIES_DIR := $(DIST_DIR)/invest
INVEST_BINARIES_DIR_ZIP := $(OSNAME)_invest_binaries.zip
APIDOCS_HTML_DIR := $(DIST_DIR)/apidocs
APIDOCS_ZIP_FILE := $(DIST_DIR)/InVEST_$(VERSION)_apidocs.zip
USERGUIDE_HTML_DIR := $(DIST_DIR)/userguide
Expand Down Expand Up @@ -389,6 +392,7 @@ signcode_windows:
@echo "Installer was signed with signtool"

deploy:
-(cd $(INVEST_BINARIES_DIR) && $(ZIP) -r ../$(INVEST_BINARIES_DIR_ZIP) .)
-$(GSUTIL) -m rsync $(DIST_DIR) $(DIST_URL_BASE)
-$(GSUTIL) -m rsync -r $(DIST_DIR)/data $(DIST_URL_BASE)/data
-$(GSUTIL) -m rsync -r $(DIST_DIR)/userguide $(DIST_URL_BASE)/userguide
Expand Down
1 change: 1 addition & 0 deletions requirements-gui.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ qtpy>1.3 # pip-only
qtawesome # pip-only
requests
PySide2!=5.15.0 # pip-only
Flask
12 changes: 11 additions & 1 deletion src/natcap/invest/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def __call__(self, parser, namespace, values, option_string=None):


def main(user_args=None):
"""CLI entry point for launching InVEST runs.
"""CLI entry point for launching InVEST runs and other useful utilities.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of finest docs work, right there. Haha, I didn't feel like trying to duplicate all the parser helptext that follows and explains all the other entry-points.


This command-line interface supports two methods of launching InVEST models
from the command-line:
Expand Down Expand Up @@ -391,6 +391,12 @@ def main(user_args=None):
help=('The model for which the spec should be fetched. Use "invest '
'list" to list the available models.'))

serve_subparser = subparsers.add_parser(
'serve', help=('Start the flask app on the localhost.'))
serve_subparser.add_argument(
'--port', type=int, default=56789,
help='Port number for the Flask server')
Comment on lines +394 to +398
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!


args = parser.parse_args(user_args)

root_logger = logging.getLogger()
Expand Down Expand Up @@ -569,6 +575,10 @@ def main(user_args=None):
parser.exit(app_exitcode,
'App terminated with exit code %s\n' % app_exitcode)

if args.subcommand == 'serve':
import natcap.invest.ui_server
natcap.invest.ui_server.app.run(port=args.port)
parser.exit(0)

if __name__ == '__main__':
multiprocessing.freeze_support()
Expand Down
207 changes: 207 additions & 0 deletions src/natcap/invest/ui_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
"""A Flask app with HTTP endpoints used by the InVEST Workbench."""
import codecs
import collections
from datetime import datetime
import importlib
import json
import logging
import pprint
import textwrap

from flask import Flask
from flask import request
import natcap.invest.cli
import natcap.invest.datastack

logging.basicConfig(level=logging.DEBUG)
LOGGER = logging.getLogger(__name__)

app = Flask(__name__)

# Lookup names to pass to `invest run` based on python module names
_UI_META = collections.namedtuple('UIMeta', ['run_name', 'human_name'])
MODULE_MODELRUN_MAP = {
v.pyname: _UI_META(
run_name=k,
human_name=v.humanname)
for k, v in natcap.invest.cli._MODEL_UIS.items()}


def shutdown_server():
"""Shutdown the flask server."""
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()


@app.route('/ready', methods=['GET'])
def get_is_ready():
"""Returns something simple to confirm the server is open."""
return 'Flask ready'


@app.route('/shutdown', methods=['GET'])
def shutdown():
"""A request to this endpoint shuts down the server."""
shutdown_server()
return 'Flask server shutting down...'


@app.route('/models', methods=['GET'])
def get_invest_models():
"""Gets a list of available InVEST models.

Returns:
A JSON string
"""
LOGGER.debug('get model list')
return natcap.invest.cli.build_model_list_json()


@app.route('/getspec', methods=['POST'])
def get_invest_getspec():
"""Gets the ARGS_SPEC dict from an InVEST model.

Body (JSON string): "carbon"

Returns:
A JSON string.
"""
target_model = request.get_json()
target_module = natcap.invest.cli._MODEL_UIS[target_model].pyname
model_module = importlib.import_module(name=target_module)
LOGGER.debug(model_module.__file__)
spec = model_module.ARGS_SPEC
return json.dumps(spec)


@app.route('/validate', methods=['POST'])
def get_invest_validate():
"""Gets the return value of an InVEST model's validate function.

Body (JSON string):
model_module: string (e.g. natcap.invest.carbon)
args: JSON string of InVEST model args keys and values

Returns:
A JSON string.
"""
payload = request.get_json()
LOGGER.debug(payload)
target_module = payload['model_module']
args_dict = json.loads(payload['args'])
LOGGER.debug(args_dict)
try:
limit_to = payload['limit_to']
except KeyError:
limit_to = None
model_module = importlib.import_module(name=target_module)
results = model_module.validate(args_dict, limit_to=limit_to)
LOGGER.debug(results)
return json.dumps(results)


@app.route('/post_datastack_file', methods=['POST'])
def post_datastack_file():
"""Extracts InVEST model args from json, logfiles, or datastacks.

Body (JSON string): path to file

Returns:
A JSON string.
"""
filepath = request.get_json()
stack_type, stack_info = natcap.invest.datastack.get_datastack_info(
filepath)
run_name, human_name = MODULE_MODELRUN_MAP[stack_info.model_name]
result_dict = {
'type': stack_type,
'args': stack_info.args,
'module_name': stack_info.model_name,
'model_run_name': run_name,
'model_human_name': human_name,
'invest_version': stack_info.invest_version
}
LOGGER.debug(result_dict)
return json.dumps(result_dict)


@app.route('/write_parameter_set_file', methods=['POST'])
def write_parameter_set_file():
"""Writes InVEST model args keys and values to a datastack JSON file.

Body (JSON string):
parameterSetPath: string
moduleName: string(e.g. natcap.invest.carbon)
args: JSON string of InVEST model args keys and values
relativePaths: boolean

Returns:
A string.
"""
payload = request.get_json()
filepath = payload['parameterSetPath']
modulename = payload['moduleName']
args = json.loads(payload['args'])
relative_paths = payload['relativePaths']

natcap.invest.datastack.build_parameter_set(
args, modulename, filepath, relative=relative_paths)
return 'parameter set saved'


# Borrowed this function from natcap.invest.model because I assume
# that module won't persist if we eventually deprecate the Qt UI.
@app.route('/save_to_python', methods=['POST'])
def save_to_python():
"""Writes a python script with a call to an InVEST model execute function.

Body (JSON string):
filepath: string
modelname: string (e.g. carbon)
pyname: string (e.g. natcap.invest.carbon)
args_dict: JSON string of InVEST model args keys and values

Returns:
A string.
"""
payload = request.get_json()
save_filepath = payload['filepath']
modelname = payload['modelname']
pyname = payload['pyname']
args_dict = json.loads(payload['args'])

script_template = textwrap.dedent("""\
# coding=UTF-8
# -----------------------------------------------
# Generated by InVEST {invest_version} on {today}
# Model: {modelname}

import {py_model}

args = {model_args}

if __name__ == '__main__':
{py_model}.execute(args)
""")

with codecs.open(save_filepath, 'w', encoding='utf-8') as py_file:
# cast_args = dict((unicode(key), value) for (key, value)
# in args_dict.items())
args = pprint.pformat(args_dict, indent=4) # 4 spaces

# Tweak formatting from pprint:
# * Bump parameter inline with starting { to next line
# * add trailing comma to last item item pair
# * add extra space to spacing before first item
args = args.replace('{', '{\n ')
args = args.replace('}', ',\n}')
py_file.write(script_template.format(
invest_version=natcap.invest.cli.__version__,
today=datetime.now().strftime('%c'),
modelname=modelname,
py_model=pyname,
model_args=args))

return 'python script saved'
20 changes: 20 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,23 @@ def test_validate_fisheries_json(self):
# Validation returned successfully, so error code 0 even though there
# are warnings.
self.assertEqual(exit_cm.exception.code, 0)

def test_serve(self):
"""CLI: serve entry-point exists; flask app can import."""
from natcap.invest import cli

with unittest.mock.patch('natcap.invest.ui_server.app.run',
return_value=None) as patched_app:
with self.assertRaises(SystemExit) as exit_cm:
cli.main(['serve'])
self.assertEqual(exit_cm.exception.code, 0)

def test_serve_port_argument(self):
"""CLI: serve entry-point parses port subargument."""
from natcap.invest import cli

with unittest.mock.patch('natcap.invest.ui_server.app.run',
return_value=None) as patched_app:
with self.assertRaises(SystemExit) as exit_cm:
cli.main(['serve', '--port', '12345'])
self.assertEqual(exit_cm.exception.code, 0)