From 96d1e94954b4a8b549cf338c969a7c584f5721f1 Mon Sep 17 00:00:00 2001 From: cliffckerr Date: Sat, 21 Mar 2020 10:38:06 -0700 Subject: [PATCH] rethink requirements structure --- covasim/__init__.py | 48 ++--------- covasim/requirements.py | 165 +++++++++++++++++++++++-------------- covasim/webapp/cova_app.py | 14 ++-- requirements.txt | 3 +- setup.py | 8 ++ 5 files changed, 127 insertions(+), 111 deletions(-) diff --git a/covasim/__init__.py b/covasim/__init__.py index 2208a6e99..43decaa84 100644 --- a/covasim/__init__.py +++ b/covasim/__init__.py @@ -3,52 +3,14 @@ #%% Print version and license information from .version import __version__, __versiondate__, __license__ +from . import requirements as _requirements print(__license__) -#%% Check imports -- note, must be manually updated to match requirements.txt unfortunately! - -_min_sciris_version = '0.16.0' - - -# Check Sciris -try: - import sciris as _sc -except ImportError: - raise ImportError('Sciris not found; please install via "pip install sciris"') -if _sc.compareversions(_sc.__version__, _min_sciris_version) < 0: - raise ImportError(f'Sciris {_sc.__version__} is incompatible; please upgrade via "pip install sciris=={_min_sciris_version}"') - - - -# Check health systems -- optional dependency -try: - import covid_healthsystems as _hsys_available - _hsys_available = True -except ImportError as E: - print(f'Warning: covid_healthsystems is not available. Hospital capacity analyses will not be available. (Error: {str(E)})\n') - _hsys_available = False - - -# Check synthpops -- optional dependency -try: - import synthpops as _synth_available - _synth_available = True -except ImportError as E: - print(f'Warning: synthpops is not available. Detailed demographic data will not be available. (Error: {str(E)})\n') - _synth_available = False - -# Check parestlib -- optional dependency -try: - import parestlib as _parest_available - _parest_available = True -except ImportError as E: - print(f'Warning: parestlib is not available. Automated calibration will not be available. (Error: {str(E)})\n') - _parest_available = False - - -# Tidy up temporary variables, leaving _hsys and _parest since these are used later -del _min_sciris_version, _sc +#%% Check that requirements are met +_requirements.check_sciris() +_requirements.check_scirisweb(die=False) +_requirements.check_extra_libs() #%% Imports from here -- just the framework, basic functions, and base -- the "base" version diff --git a/covasim/requirements.py b/covasim/requirements.py index b934df392..e5eb4faf4 100644 --- a/covasim/requirements.py +++ b/covasim/requirements.py @@ -1,75 +1,118 @@ +#%% Housekeeping + import os -_min_sciris_version = '0.16.0' +__all__ = ['available', 'min_versions', 'get_min_versions', 'check_sciris', 'check_scirisweb', 'check_extra_libs'] -class Available: - ''' Store information about optional imports ''' - scirisweb = None - healthsystems = None - synthpops = None - parestlib = None -available = Available() # Make this available at the module level +available = {} # Make this available at the module level min_versions = {} -def get_requirements(): +#%% Get the right versions + +def get_min_versions(): filename = 'requirements.txt' + comparator = '>=' cwd = os.path.abspath(os.path.dirname(__file__)) - filepath = os.path.join(os.pardir, cwd, filename) + filepath = os.path.join(cwd, os.pardir, filename) + print(__file__, cwd, filepath) # Load requirements from txt file with open(filepath) as f: requirements = f.read().splitlines() + for requirement in requirements: # Expecting e.g. 'scirisweb>=0.16.0' + split = requirement.split(comparator) + if len(split) == 2: + package = split[0] + version = split[1] + min_versions[package] = version + + return # NB, modifies the module-level min_versions dict in-place + +get_min_versions() # Populate these straight away + + +#%% Check dependencies + +def check_sciris(): + ''' Check that Sciris is available and the right version ''' + try: + import sciris as sc + except ModuleNotFoundError: + raise ModuleNotFoundError('Sciris is a required dependency but is not found; please install via "pip install sciris"') + ver = sc.__version__ + minver = min_versions['sciris'] + if sc.compareversions(ver, minver) < 0: + raise ImportError(f'You have Sciris {ver} but {minver} is required; please upgrade via "pip install --upgrade sciris"') + return + + +def check_scirisweb(die=False): + ''' Check that Scirisweb is available and the right version ''' + import sciris as sc # Here since one of the purposes of this file is to check if this exists + available['scirisweb'] = True # Assume it works + import_error = '' + version_error = '' + + # Try imports + try: + import scirisweb + except ModuleNotFoundError: + import_error = 'Scirisweb not found; please install via "pip install scirisweb"' + if not import_error: + ver = scirisweb.__version__ + minver = min_versions['scirisweb'] + if sc.compareversions(ver, minver) < 0: + version_error = f'You have Scirisweb {ver} but {minver} is required; please upgrade via "pip install --upgrade scirisweb"' + + # Handle consequences + if die: + if import_error: + raise ModuleNotFoundError(import_error) + elif version_error: + raise ImportError(version_error) + else: + if import_error: + print('Warning: scirisweb was not found; webapp functionality is not available (you can install with "pip install scirisweb")') + elif version_error: + print(f'Warning: scirisweb is version {ver} but {minver} is required; webapp is disabled (fix with "pip install --upgrade scirisweb")') + + if import_error or version_error: + available['scirisweb'] = False + + return + + +def check_extra_libs(): + ''' Check whether optional dependencies are available ''' + + # Check health systems -- optional dependency + try: + import covid_healthsystems # noqa + available['covid_healthsystems'] = True + except ImportError as E: + import_error = f'Warning: covid_healthsystems is not available. Hospital capacity analyses will not be available. (Error: {str(E)})\n' + available['covid_healthsystems'] = False + print(import_error) + + # Check synthpops -- optional dependency + try: + import synthpops # noqa + available['synthpops'] = True + except ImportError as E: + import_error = f'Warning: synthpops is not available. Detailed demographic data will not be available. (Error: {str(E)})\n' + available['synthpops'] = True + print(import_error) + + # Check parestlib -- optional dependency + try: + import parestlib as _parest_available # noqa + available['parestlib'] = True + except ImportError as E: + import_error = f'Warning: parestlib is not available. Automated calibration will not be available. (Error: {str(E)})\n' + available['parestlib'] = True + print(import_error) + + return - - - -# Check Sciris -try: - import sciris as _sc -except ImportError: - raise ImportError('Sciris not found; please install via "pip install sciris"') -if _sc.compareversions(_sc.__version__, _min_sciris_version) < 0: - raise ImportError(f'Sciris {_sc.__version__} is incompatible; please upgrade via "pip install sciris=={_min_sciris_version}"') - - -_min_scirisweb_version = '0.16.0' - -# Check ScirisWeb import -try: - import scirisweb as sw -except ImportError: - raise ImportError('Scirisweb not found; please install via "pip install scirisweb"') -if _sc.compareversions(_sw.__version__, _min_scirisweb_version) < 0: - raise ImportError(f'Scirisweb {_sw.__version__} is incompatible; please upgrade via "pip install scirisweb=={_min_scirisweb_version}"') - - -# Check health systems -- optional dependency -try: - import covid_healthsystems as _hsys_available - _hsys_available = True -except ImportError as E: - print(f'Warning: covid_healthsystems is not available. Hospital capacity analyses will not be available. (Error: {str(E)})\n') - _hsys_available = False - - -# Check synthpops -- optional dependency -try: - import synthpops as _synth_available - _synth_available = True -except ImportError as E: - print(f'Warning: synthpops is not available. Detailed demographic data will not be available. (Error: {str(E)})\n') - _synth_available = False - -# Check parestlib -- optional dependency -try: - import parestlib as _parest_available - _parest_available = True -except ImportError as E: - print(f'Warning: parestlib is not available. Automated calibration will not be available. (Error: {str(E)})\n') - _parest_available = False - - -# Tidy up temporary variables, leaving _hsys and _parest since these are used later -del _min_sciris_version, _sc \ No newline at end of file diff --git a/covasim/webapp/cova_app.py b/covasim/webapp/cova_app.py index 353460d32..bbf991014 100644 --- a/covasim/webapp/cova_app.py +++ b/covasim/webapp/cova_app.py @@ -2,16 +2,18 @@ Sciris app to run the web interface. ''' -# Imports +# Key imports import os -import io import sys +import pylab as pl +import plotly.graph_objects as go +import sciris as sc +import covasim as cv + +# Download/upload-specific imports +import io import base64 import zipfile -import sciris as sc -import plotly.graph_objects as go -import pylab as pl -import covasim as cw import json # Check requirements diff --git a/requirements.txt b/requirements.txt index 626c1cd20..69fe56e88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,8 @@ scirisweb>=0.16.0 matplotlib>=2.2.2 numpy>=1.10.1 scipy>=1.2.0 -pandas numba +pandas +statsmodels gunicorn plotly_express \ No newline at end of file diff --git a/setup.py b/setup.py index 1942a91d5..bb44e294a 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,11 @@ +''' +Covasim installation. Requirements are listed in requirements.txt. There are three +options: + python setup.py develop # standard install, includes webapp, does not include optional libraries + python setup.py develop nowebapp # backend only, no webapp functionality + python setup.py develop full # full install, including optional libraries (NB: these libraries are not available publicly yet) +''' + import os import re import sys