diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9e4a997..96d24f0c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,28 +26,28 @@ jobs: - name: Install Requirements 📦 run: | python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r requirements-dev.txt + pip3 install -r requirements.txt + pip3 install -r requirements-dev.txt - name: Setup GHC App and init DB 🗃️ run: | - paver setup - echo -e "admin\ntest\ntest\nyou@example.com\nyou@example.com" | python GeoHealthCheck/models.py create + invoke setup + echo -e "admin\ntest\ntest\nyou@example.com\nyou@example.com" | python3 GeoHealthCheck/models.py create - name: Flake8 - Verify Coding Conventions ⚙️ run: flake8 - name: Load Fixtures Test Data ⚙️ - run: python GeoHealthCheck/models.py load tests/data/fixtures.json y + run: python3 GeoHealthCheck/models.py load tests/data/fixtures.json y - name: Run Probes ⚙️ - run: python GeoHealthCheck/healthcheck.py + run: python3 GeoHealthCheck/healthcheck.py - name: Run Unit Tests ⚙️ - run: python tests/run_tests.py + run: python3 tests/run_tests.py - name: Build Docs 📖 run: cd docs && make html - name: Cleanup 💯 - run: python GeoHealthCheck/models.py drop + run: python3 GeoHealthCheck/models.py drop diff --git a/GeoHealthCheck/__init__.py b/GeoHealthCheck/__init__.py index e1614da1..2246184a 100644 --- a/GeoHealthCheck/__init__.py +++ b/GeoHealthCheck/__init__.py @@ -27,7 +27,7 @@ # # ================================================================= -from util import read +from .util import read def get_package_version(file_): diff --git a/GeoHealthCheck/manage.py b/GeoHealthCheck/manage.py index 82c029df..03e5493c 100644 --- a/GeoHealthCheck/manage.py +++ b/GeoHealthCheck/manage.py @@ -6,7 +6,7 @@ # # Usage: # -# $ python manage.py --help +# $ python3 manage.py --help # usage: manage.py [-h] {shell,db,runserver} ... # # positional arguments: @@ -19,7 +19,7 @@ # -h, --help show this help message and exit # # For DB management: -# $ python manage.py db --help +# $ python3 manage.py db --help # usage: Perform database migrations # # positional arguments: diff --git a/GeoHealthCheck/migrations/README.md b/GeoHealthCheck/migrations/README.md index 81db6740..38d29b47 100755 --- a/GeoHealthCheck/migrations/README.md +++ b/GeoHealthCheck/migrations/README.md @@ -6,7 +6,7 @@ and Flask-Script. Users should be able to upgrade existing installs via: # In top dir of installation - paver upgrade + invoke upgrade The `versions` dir contains the various upgrades. These were initially created using the Alembic `autogenerate` facility @@ -20,12 +20,12 @@ for various DB management tasks related to migrations and upgrading. Whenever a change in the database schema or table content conventions has changed a new migration should be created via the command. - python manage.py db migrate + python3 manage.py db migrate Where `migrate` is an alias for `revision --autogenerate`. Alternatively if the autogeneration does not work, create an empty migration: - python manage.py db revision + python3 manage.py db revision In both cases this will create a new revision and a `_.py` file under `versions/` to upgrade @@ -37,9 +37,9 @@ to check various DB metadata. Subsequently the upgrade can be performed using: - python manage.py db upgrade + python3 manage.py db upgrade # or the equivalent (for users) - paver upgrade + invoke upgrade ## Revisions diff --git a/GeoHealthCheck/models.py b/GeoHealthCheck/models.py index 6f1b44ec..746af71c 100644 --- a/GeoHealthCheck/models.py +++ b/GeoHealthCheck/models.py @@ -986,7 +986,7 @@ def db_commit(): elif sys.argv[1] == 'run': print('NOTICE: models.py no longer here.') - print('Use: python healthcheck.py or upcoming cli.py') + print('Use: python3 healthcheck.py or upcoming cli.py') elif sys.argv[1] == 'flush': flush_runs() diff --git a/GeoHealthCheck/util.py b/GeoHealthCheck/util.py index 6ca5b764..19045b4d 100644 --- a/GeoHealthCheck/util.py +++ b/GeoHealthCheck/util.py @@ -38,8 +38,9 @@ from urllib.parse import urlparse from gettext import translation from passlib.hash import pbkdf2_sha256 -from factory import Factory -from init import App + +from GeoHealthCheck.factory import Factory +from GeoHealthCheck.init import App from jinja2 import Environment, FileSystemLoader diff --git a/README.md b/README.md index aa7573cc..53942d5d 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,15 @@ virtualenv GeoHealthCheck && cd $_ . bin/activate git clone https://github.com/geopython/GeoHealthCheck.git cd GeoHealthCheck -pip install Paver +pip3 install Invoke # setup installation -paver setup +invoke setup # generate secret key -paver create_secret_key +invoke create_secret_key # setup local configuration (overrides GeoHealthCheck/config_main.py) vi instance/config_site.py # edit at least secret key: -# - SECRET_KEY # copy/paste result string from paver create_secret_key +# - SECRET_KEY # copy/paste result string from `invoke create_secret_key` # Optional: edit other settings or leave defaults # - SQLALCHEMY_DATABASE_URI @@ -37,32 +37,32 @@ vi instance/config_site.py # - GHC_MAP (or use default settings) # setup database and superuser account interactively -paver create +invoke create # start webserver with healthcheck runner daemon inside # (default is 0.0.0.0:8000) -python GeoHealthCheck/app.py +python3 GeoHealthCheck/app.py # or start webserver on another port -python GeoHealthCheck/app.py 0.0.0.0:8881 +python3 GeoHealthCheck/app.py 0.0.0.0:8881 # or start webserver on another IP -python GeoHealthCheck/app.py 192.168.0.105:8001 +python3 GeoHealthCheck/app.py 192.168.0.105:8001 # OR start webserver and separate runner daemon (scheduler) process vi instance/config_site.py # GHC_RUNNER_IN_WEBAPP = False -python GeoHealthCheck/scheduler.py & -python GeoHealthCheck/app.py +python3 GeoHealthCheck/scheduler.py & +python3 GeoHealthCheck/app.py # next: use a real webserver or preferably Docker for production # other commands # # drop database -python GeoHealthCheck/models.py drop +python3 GeoHealthCheck/models.py drop # load data in database (WARN: deletes existing data!) # See example data .json files in tests/data -python GeoHealthCheck/models.py load <.json data file> [y/n] +python3 GeoHealthCheck/models.py load <.json data file> [y/n] ``` diff --git a/docker/README.md b/docker/README.md index 2a8d8199..b97f33a3 100644 --- a/docker/README.md +++ b/docker/README.md @@ -234,11 +234,11 @@ docker exec -it docker_geohealthcheck_1 bash source /venv/bin/activate . cd /GeoHealthCheck/ -# next can use Paver commands e.g. DB upgrade -paver upgrade +# next can use Invoke commands e.g. DB upgrade +invoke upgrade etc ``` -NB: database upgrades (`paver upgrade`) +NB: database upgrades (`invoke upgrade`) are always performed automatically when running GHC via Docker. diff --git a/docker/scripts/configure.sh b/docker/scripts/configure.sh index 980faa5e..1d54c908 100755 --- a/docker/scripts/configure.sh +++ b/docker/scripts/configure.sh @@ -15,7 +15,7 @@ function create_db() { pushd /GeoHealthCheck/ || exit 1 source bin/activate - paver create -u ${ADMIN_NAME} -p ${ADMIN_PWD} -e ${ADMIN_EMAIL} + invoke create -u ${ADMIN_NAME} -p ${ADMIN_PWD} -e ${ADMIN_EMAIL} popd || exit 1 } diff --git a/docker/scripts/cron-jobs-hourly.sh b/docker/scripts/cron-jobs-hourly.sh index 9e133f90..2a028355 100755 --- a/docker/scripts/cron-jobs-hourly.sh +++ b/docker/scripts/cron-jobs-hourly.sh @@ -7,4 +7,4 @@ then cp -ar /plugins/* /GeoHealthCheck/GeoHealthCheck/plugins/ fi -python /GeoHealthCheck/GeoHealthCheck/healthcheck.py +python3 /GeoHealthCheck/GeoHealthCheck/healthcheck.py diff --git a/docker/scripts/install.sh b/docker/scripts/install.sh index 5f448c93..21604d6e 100755 --- a/docker/scripts/install.sh +++ b/docker/scripts/install.sh @@ -12,10 +12,10 @@ source bin/activate # Docker-specific deps -pip install --no-cache-dir -r docker/scripts/requirements.txt +pip3 install --no-cache-dir -r docker/scripts/requirements.txt # Sets up GHC itself -paver setup +invoke setup mv /config_site.py /GeoHealthCheck/instance/config_site.py # Copy possible Plugins into app tree diff --git a/docker/scripts/requirements.txt b/docker/scripts/requirements.txt index ae24e668..021febe6 100644 --- a/docker/scripts/requirements.txt +++ b/docker/scripts/requirements.txt @@ -1 +1 @@ -Paver==1.3.4 +invoke==3.0.3 diff --git a/docker/scripts/run-runner.sh b/docker/scripts/run-runner.sh index 4b559027..3c91b76b 100755 --- a/docker/scripts/run-runner.sh +++ b/docker/scripts/run-runner.sh @@ -13,6 +13,6 @@ export PYTHONPATH=/GeoHealthCheck/GeoHealthCheck:$PYTHONPATH cd /GeoHealthCheck source bin/activate -paver runner_daemon +invoke runner_daemon echo "END /run-runner.sh" diff --git a/docker/scripts/run-tests.sh b/docker/scripts/run-tests.sh index d75693c7..d66621dc 100755 --- a/docker/scripts/run-tests.sh +++ b/docker/scripts/run-tests.sh @@ -18,6 +18,6 @@ export PYTHONPATH=/GeoHealthCheck/GeoHealthCheck:$PYTHONPATH cd /GeoHealthCheck source bin/activate -paver run_tests +invoke run_tests echo "END /run-tests.sh" diff --git a/docker/scripts/run-web.sh b/docker/scripts/run-web.sh index 47bcbaf1..4cf6c56d 100755 --- a/docker/scripts/run-web.sh +++ b/docker/scripts/run-web.sh @@ -17,7 +17,7 @@ pushd /GeoHealthCheck || exit 1 source bin/activate -paver upgrade +invoke upgrade # SCRIPT_NAME should not have value '/' [ "${SCRIPT_NAME}" = '/' ] && export SCRIPT_NAME="" && echo "make SCRIPT_NAME empty from /" @@ -31,6 +31,6 @@ exec gunicorn --pythonpath /GeoHealthCheck/lib/python3.12/site-packages/ --worke GeoHealthCheck.app:APP # Built-in Flask server, deprecated -# python /GeoHealthCheck/GeoHealthCheck/app.py ${HOST}:${PORT} +# python3 /GeoHealthCheck/GeoHealthCheck/app.py ${HOST}:${PORT} echo "END /run-web.sh" diff --git a/docs/admin.rst b/docs/admin.rst index cae7bd34..d4dfcc4e 100644 --- a/docs/admin.rst +++ b/docs/admin.rst @@ -21,7 +21,7 @@ To create the database execute the following: Open a command line, (if needed activate your virtualenv), and do :: - python GeoHealthCheck/models.py create + python3 GeoHealthCheck/models.py create drop db ....... @@ -30,7 +30,7 @@ To delete the database execute the following, however you will loose all your in Open a command line, (if needed activate your virtualenv), and do :: - python GeoHealthCheck/models.py drop + python3 GeoHealthCheck/models.py drop Note: you need to create a Database again before you can start GHC again. @@ -39,7 +39,7 @@ load data To load a JSON data file, do (WARN: deletes existing data!) :: - python GeoHealthCheck/models.py load [y/n] + python3 GeoHealthCheck/models.py load [y/n] Hint: see `tests/data` for example JSON data files. @@ -75,10 +75,10 @@ a password is forgotten and somehow the email-based reset is not available nor w In that case, password-hashes can be created from the command-line using the Python library `passlib `_ within an interactive Python-shell as follows: :: - $ pip install passlib + $ pip3 install passlib # or in Debian/Ubuntu: apt-get install python-passlib - python + python3 >>> from passlib.hash import pbkdf2_sha256 >>> >>> hash = pbkdf2_sha256.hash("mynewpassword") @@ -93,10 +93,9 @@ Or more compact within the root dir of your GHC installation: :: >>> create_hash('mynewpassword') '$pbkdf2-sha256$29000$8X4PAUAIAcC4V2rNea9Vqg$XnMx1SfEiBzBAMOQOOC7uxCcyzVuKaHENLj3IfXvfu0' -Or even more compact within the root dir of your GHC installation via Paver: :: +Or even more compact within the root dir of your GHC installation via Invoke: :: - $ paver create_hash -p mypass - ---> pavement.create_hash + $ invoke create_hash -p mypass Copy/paste the entire token below for example to set password $pbkdf2-sha256$29000$FkJoTYnxPqc0pjQG4HxP6Q$C3SZb8jqtM7zKS1DSLcouc/CL9XMI9cL5xT6DRTOEd4 diff --git a/docs/config.rst b/docs/config.rst index cd218b25..8914d802 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -15,7 +15,7 @@ The configuration options are: - **SQLALCHEMY_DATABASE_URI**: the database configuration. See the SQLAlchemy documentation for more info - **SQLALCHEMY_ENGINE_OPTION_PRE_PING**: DB Disconnect Handling, emitting a test statement on the SQL connection at the start of each connection pool checkout (default: ``False``) -- **SECRET_KEY**: secret key to set when enabling authentication. Use the output of ``paver create_secret_key`` to set this value +- **SECRET_KEY**: secret key to set when enabling authentication. Use the output of ``invoke create_secret_key`` to set this value - **GHC_RETENTION_DAYS**: the number of days to keep Run history - **GHC_PROBE_HTTP_TIMEOUT_SECS**: stop waiting for the first byte of a Probe response after the given number of seconds - **GHC_MINIMAL_RUN_FREQUENCY_MINS**: minimal run frequency for Resource that can be set in web UI @@ -51,7 +51,7 @@ The configuration options are: Example on overriding the configuration with an environment variable: :: export GHC_SETTINGS=/tmp/my_GHC_settings.py - paver run_tests + invoke run_tests As an example: the `my_GHC_settings.py` file can contain a single line to define a test database: :: @@ -94,9 +94,9 @@ Scheduling via Cron Edit the file ``jobs.cron`` so that the paths reflect the path to the virtualenv. Set the first argument to the desired monitoring time step. If finished editing, -copy the command line calls e.g. ``/YOURvirtualenv/bin_or_SCRIPTSonwindows/python /path/to/GeoHealthCheck/GeoHealthCheck/healthcheck.py run`` +copy the command line calls e.g. ``/YOURvirtualenv/bin_or_SCRIPTSonwindows/python3 /path/to/GeoHealthCheck/GeoHealthCheck/healthcheck.py run`` to the commandline to test if they work sucessfully. -On Windows - do not forget to include the ''.exe.'' file extension to the python executable. +On Windows - do not forget to include the ''.exe.'' file extension to the python3 executable. For documentation how to create cron jobs see your operating system: on \*NIX systems e.g. ``crontab -e`` and on windows e.g. the `nssm `_. @@ -111,7 +111,7 @@ This is the preferred mode as each `Resource` can have its own schedule (configu via Dashboard) and `cron` has dependencies on local environment. Later versions may phase out cron-scheduling completely. -The **GHC Runner** can be run via the command `paver runner_daemon` or can run internally within +The **GHC Runner** can be run via the command `invoke runner_daemon` or can run internally within the **GHC Webapp** by setting the config variable **GHC_RUNNER_IN_WEBAPP** to `True` (the default). NB it is still possible to run GHC as in the pre-v0.5.0 mode using cron-jobs: just run the **GHC Webapp** with **GHC_RUNNER_IN_WEBAPP** set to `False` and have your cron-jobs scheduled. @@ -153,7 +153,7 @@ Compiling Language Files ........................ At runtime compiled versions, `.mo` files, of the language-files are used. -Easiest to compile is via: `paver compile_translations` in the project root dir. +Easiest to compile is via: `invoke compile_translations` in the project root dir. This basically calls `pybabel compile` with the proper options. Now you can e.g. test your new translations by starting GHC. @@ -175,7 +175,7 @@ Missing translations will have `msgstr ""` like in this excerpt: :: Next all empty `msgstr` can be filled. -Updating is easiest using the command `paver update_translations` within the root dir of the project. +Updating is easiest using the command `invoke update_translations` within the root dir of the project. This will basically call `pybabel extract` followed by `pybabel update` with the proper parameters. Customizing the Score Matrix diff --git a/docs/install.rst b/docs/install.rst index 3efa37df..f26a1332 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -27,12 +27,12 @@ GeoHealthCheck is built on the awesome Flask micro-framework and uses `APScheduler` is used to run scheduled healthchecks. -These dependencies are automatically installed (see below). ``Paver`` is used +These dependencies are automatically installed (see below). `Invoke `_ is used for installation and management. ``Cron`` was used for scheduling the actual healthchecks before v0.5.0. -Starting from version v0.8.0.0 GeoHealthCheck requires **python 3**. Previous -versions require **python 2**. GeoHealthCheck is at least compatible with Python versions +Starting from version v0.8.0.0 GeoHealthCheck requires **python3 3**. Previous +versions require **python3 2**. GeoHealthCheck is at least compatible with Python versions up to and including `3.12.3`. Higher Python versions may work but are untested. Install @@ -56,20 +56,20 @@ Install git clone https://github.com/geopython/GeoHealthCheck.git cd GeoHealthCheck - # install paver dependency for admin tool - pip install Paver + # install Invoke dependency for admin tool + pip3 install invoke # setup app - paver setup + invoke setup # create secret key to use for auth - paver create_secret_key + invoke create_secret_key # almost there! Customize config vi instance/config_site.py # edit: # - SQLALCHEMY_DATABASE_URI - # - SECRET_KEY # from paver create_secret_key + # - SECRET_KEY # from invoke create_secret_key # - GHC_RETENTION_DAYS # - GHC_SELF_REGISTER # - GHC_NOTIFICATIONS @@ -85,10 +85,10 @@ Install # - GEOIP # or use the default settings # init database - python GeoHealthCheck/models.py create + python3 GeoHealthCheck/models.py create # start web-app - python GeoHealthCheck/app.py # http://localhost:8000/ + python3 GeoHealthCheck/app.py # http://localhost:8000/ # when you are done, you can exit the virtualenv deactivate @@ -105,15 +105,15 @@ An existing GHC database installation can be upgraded with: .. code-block:: bash # In the top directory (e.g. the topdir cloned from github) - paver upgrade + invoke upgrade # Notice any output, in particular errors Notes: * **Always backup your database first!!** -* make sure Flask-Migrate is installed (see requirements.txt), else: `pip install Flask-Migrate==2.5.2`, but best is to run `paver setup` also for other dependencies -* upgrading is "smart": you can always run `paver upgrade`, it has no effect when DB is already up to date +* make sure Flask-Migrate is installed (see requirements.txt), else: `pip3 install Flask-Migrate==2.5.2`, but best is to run `invoke setup` also for other dependencies +* upgrading is "smart": you can always run `invoke upgrade`, it has no effect when DB is already up to date * when upgrading from earlier versions without Plugin-support: - adapt your `config_site.py` to Plugin settings from `config_main.py` @@ -121,7 +121,7 @@ Notes: When running with Docker see the `GHC Docker Readme `_ -how to run `paver upgrade` within your Docker Container. +how to run `invoke upgrade` within your Docker Container. Upgrade notes v0.5.0 .................... @@ -134,7 +134,7 @@ Upgrade notes v0.6.0 .................... In GHC v0.6.0 encryption was added for password storage. Existing passwords should be migrated via -the `paver upgrade` command. Also password recovery was changed: a user can create a new password via +the `invoke upgrade` command. Also password recovery was changed: a user can create a new password via a unique, personal URL that GHC sends by email. This requires a working email configuration and a reachable `SITE_URL` config value. See :ref:`admin_user_mgt` for solving password problems. @@ -192,9 +192,9 @@ Start using Flask's built-in WSGI server: .. code-block:: bash - python GeoHealthCheck/app.py # http://localhost:8000 - python GeoHealthCheck/app.py 0.0.0.0:8881 # http://localhost:8881 - python GeoHealthCheck/app.py 192.168.0.105:8957 # http://192.168.0.105:8957 + python3 GeoHealthCheck/app.py # http://localhost:8000 + python3 GeoHealthCheck/app.py 0.0.0.0:8881 # http://localhost:8881 + python3 GeoHealthCheck/app.py 192.168.0.105:8957 # http://192.168.0.105:8957 This runs the (Flask) **GHC Webapp**, by default with the **GHC Runner** (scheduled healthchecker) internally. @@ -205,10 +205,10 @@ From the command-line run both processes, e.g. in background or different termin .. code-block:: bash # run GHC Runner, here in background - python GeoHealthCheck/scheduler.py & + python3 GeoHealthCheck/scheduler.py & # run GHC Webapp for http://localhost:8000 - python GeoHealthCheck/app.py + python3 GeoHealthCheck/app.py To enable in Apache, use ``GeoHealthCheck.wsgi`` and configure in Apache @@ -272,8 +272,8 @@ to run the scheduled healthchecks. Use virtualenv .............. -This is a general Python-recommendation. Save yourself from classpath and library hells by using `virtualenv`! Starting with python 3.3 -a `venv script `_ is provided and from python 3.6 the `venv module `_ +This is a general Python-recommendation. Save yourself from classpath and library hells by using `virtualenv`! Starting with python3 3.3 +a `venv script `_ is provided and from python3 3.6 the `venv module `_ is included in the standard library. Use SSL (HTTPS) diff --git a/jobs.cron b/jobs.cron index a60c13fc..7b0bacd3 100644 --- a/jobs.cron +++ b/jobs.cron @@ -1,2 +1,2 @@ -@hourly /virtualenv/bin/python /path/to/GeoHealthCheck/GeoHealthCheck/healthcheck.py run -@daily /virtualenv/bin/python /path/to/GeoHealthCheck/GeoHealthCheck/models.py flush +@hourly /virtualenv/bin/python3 /path/to/GeoHealthCheck/GeoHealthCheck/healthcheck.py run +@daily /virtualenv/bin/python3 /path/to/GeoHealthCheck/GeoHealthCheck/models.py flush diff --git a/pavement.py b/pavement.py deleted file mode 100644 index d4cd1e4f..00000000 --- a/pavement.py +++ /dev/null @@ -1,326 +0,0 @@ -# ================================================================= -# -# Authors: Tom Kralidis -# -# Copyright (c) 2023 Tom Kralidis -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -# -# ================================================================= - -import codecs -import glob -import os -import shutil -import tempfile -from io import BytesIO -from urllib.request import urlopen -import zipfile - -from paver.easy import (Bunch, call_task, cmdopts, info, options, - path, pushd, sh, task) - -BASEDIR = os.path.abspath(os.path.dirname(__file__)) - -options( - base=Bunch( - home=path(BASEDIR), - docs=path('%s/docs' % BASEDIR), - instance=path('%s/instance' % BASEDIR), - pot=path('%s/GeoHealthCheck/translations/en/LC_MESSAGES/messages.po' % - BASEDIR), - static_docs=path('%s/GeoHealthCheck/static/docs' % BASEDIR), - static_lib=path('%s/GeoHealthCheck/static/lib' % BASEDIR), - tmp=path(tempfile.mkdtemp()), - translations=path('%s/GeoHealthCheck/translations' % BASEDIR) - ), -) - - -@task -def setup(): - """setup plugin dependencies""" - - config_file = options.base.home / 'GeoHealthCheck/config_main.py' - config_site = options.base.instance / 'config_site.py' - - # setup dirs - if not os.path.exists(options.base.static_lib): - options.base.static_lib.mkdir() - if not os.path.exists(options.base.instance): - options.base.instance.mkdir() - data_dir = options.base.instance / 'data' - data_dir.mkdir() - # data_dir.chmod(0777) gives failure on Python 2.7 Paver 1.2.1 - os.chmod(path(data_dir), 0o777) - # setup config - config_file.copy(config_site) - - # setup deps - sh('pip install -r requirements.txt') - - skin = 'http://github.com/BlackrockDigital/startbootstrap-sb-admin-2/archive/v3.3.7+1.zip' # noqa - - skin_dirs = ['dist', 'vendor'] - need_to_fetch = False - - for skin_dir in skin_dirs: - skin_dir_path = os.sep.join( - ['startbootstrap-sb-admin-2-3.3.7-1', skin_dir]) - if not os.path.exists(skin_dir_path): - need_to_fetch = True - - if need_to_fetch: - zipstr = BytesIO(urlopen(skin).read()) - zipfile_obj = zipfile.ZipFile(zipstr) - zipfile_obj.extractall(options.base.static_lib) - - for zf_mem in skin_dirs: - src_loc = path(options.base.static_lib / - 'startbootstrap-sb-admin-2-3.3.7-1' / zf_mem) - dest_loc = path(options.base.static_lib / zf_mem) - if not os.path.exists(dest_loc): - src_loc.move(dest_loc) - else: - info('directory already exists. Skipping') - - shutil.rmtree(path(options.base.static_lib / - 'startbootstrap-sb-admin-2-3.3.7-1')) - - # install bootstrap-tagsinput to static/lib - info('Getting select2') - select2 = 'https://github.com/select2/select2/archive/4.0.3.zip' - - zipstr = BytesIO(urlopen(select2).read()) - zipfile_obj = zipfile.ZipFile(zipstr) - zipfile_obj.extractall(options.base.static_lib) - dirname = glob.glob(options.base.static_lib / 'select2-*')[0] - dstdir = ''.join(dirname.rsplit('-', 1)[:-1]) - try: - os.rename(dirname, dstdir) - except OSError: - shutil.rmtree(dstdir) - os.rename(dirname, dstdir) - - # install leafletjs to static/lib - info('Getting leaflet') - leaflet_cdn_url = 'https://leafletjs-cdn.s3.amazonaws.com/content/leaflet' - leafletjs = '{}/v1.9.4/leaflet.zip'.format(leaflet_cdn_url) - - zipstr = BytesIO(urlopen(leafletjs).read()) - zipfile_obj = zipfile.ZipFile(zipstr) - zipfile_obj.extractall(options.base.static_lib / 'leaflet') - - # install html5shiv to static/lib - cdn_url = 'https://cdn.jsdelivr.net/npm' - with open(path(options.base.static_lib / 'html5shiv.min.js'), 'w') as f: - url = \ - '{}/html5shiv.min.js@3.7.2/html5shiv.min.js'.format(cdn_url) - content = urlopen(url).read().decode() - f.write(content) - - # install respond to static/lib - with open(path(options.base.static_lib / 'respond.min.js'), 'w') as f: - url = '{}/respond.min.js@1.4.2/respond.min.js'.format(cdn_url) - content = urlopen(url).read().decode() - f.write(content) - - # build i18n .mo files - call_task('compile_translations') - - # build local docs - call_task('refresh_docs') - - # message user - info('GeoHealthCheck is now built. Edit settings in %s' % config_site) - info('before deploying the application. Alternatively, you can start a') - info('development instance with "python GeoHealthCheck/app.py"') - - -@task -def create_secret_key(): - """create secret key for SECRET_KEY in instance/config_site.py""" - info('Secret key: \'%s\'' % codecs.encode(os.urandom(24), 'hex').decode()) - info('Copy/paste this key to set the SECRET_KEY') - info('value in instance/config_site.py') - - -@task -@cmdopts([ - ('email=', 'e', 'email'), - ('username=', 'u', 'username'), - ('password=', 'p', 'password') -]) -def create(options): - """create database objects and superuser account""" - - args = '' - username = options.get('username', None) - password = options.get('password', None) - email = options.get('email', None) - - if all([username, password, email]): - args = '%s %s %s' % (username, password, email) - sh('python3 %s create %s' % (path('GeoHealthCheck/models.py'), args)) - - -@task -@cmdopts([ - ('password=', 'p', 'password') -]) -def create_hash(options): - """ - Create hash, mainly for passwords. - Usage: paver create_hash -p mypass - """ - - import sys - sys.path.insert(0, BASEDIR + '/GeoHealthCheck') - from util import create_hash - token = create_hash(options.get('password', None)) - info('Copy/paste the entire token below for example to set password') - info(token) - - -@task -def upgrade(): - """upgrade database if changed; be sure to backup first!""" - - info('Upgrading database...') - with pushd(path('%s/GeoHealthCheck' % BASEDIR)): - sh('python3 manage.py db upgrade') - - -@task -def create_wsgi(): - """create WSGI wrapper and Apache2 configuration""" - wsgi_script = '%s%sGeoHealthCheck.wsgi' % (options.base.instance, os.sep) - with open(wsgi_script, 'w') as ff: - ff.write('import sys\n') - ff.write('sys.path.insert(0, \'%s\')\n' % BASEDIR) - ff.write('from GeoHealthCheck.app import APP as application') - - wsgi_conf = '%s%sGeoHealthCheck.conf' % (options.base.instance, os.sep) - with open(wsgi_conf, 'w') as ff: - ff.write('WSGIScriptAlias / %s%sGeoHealthCheck.wsgi\n' % - (options.base.instance, os.sep)) - ff.write('\n' % (BASEDIR, os.sep)) - ff.write('Order deny,allow\n') - ff.write('Allow from all\n') - ff.write('') - - -@task -def refresh_docs(): - """Build sphinx docs from scratch""" - - make = sphinx_make() - - if os.path.exists(options.base.static_docs): - shutil.rmtree(options.base.static_docs) - - with pushd(options.base.docs): - sh('%s clean' % make) - sh('%s html' % make) - source_html_dir = path('%s/docs/_build/html' % BASEDIR) - source_html_dir.copytree(options.base.static_docs) - - -@task -def clean(): - """clean environment""" - - if os.path.exists(options.base.static_lib): - shutil.rmtree(options.base.static_lib) - if os.path.exists(options.base.tmp): - shutil.rmtree(options.base.tmp) - if os.path.exists(options.base.static_docs): - shutil.rmtree(options.base.static_docs) - - -@task -def extract_translations(): - """extract translations wrapped in _() or gettext()""" - - pot_dir = path('GeoHealthCheck/translations/en/LC_MESSAGES') - if not os.path.exists(pot_dir): - pot_dir.makedirs() - - sh('pybabel extract -F babel.cfg -o %s GeoHealthCheck' % options.base.pot) - - -@task -@cmdopts([ - ('lang=', 'l', '2-letter language code'), -]) -def add_language_catalogue(options): - """adds new language profile""" - - lang = options.get('lang', None) - - if lang is None: - raise RuntimeError('missing lang argument') - - sh('pybabel init -i %s -d %s -l %s' % ( - options.base.pot, options.base.translations, lang)) - - -@task -def compile_translations(): - """build .mo files""" - - sh('pybabel compile -d %s' % options.base.translations) - - -@task -def update_translations(): - """update language strings""" - - call_task('extract_translations') - sh('pybabel update -i %s -d %s' % ( - options.base.pot, options.base.translations)) - - -@task -def runner_daemon(): - """Run the HealthCheck runner daemon scheduler""" - sh('python3 %s' % path('GeoHealthCheck/scheduler.py')) - - -@task -def run_healthchecks(): - """Run all HealthChecks directly""" - sh('python3 %s' % path('GeoHealthCheck/healthcheck.py')) - - -def sphinx_make(): - """return what command Sphinx is using for make""" - - if os.name == 'nt': - return 'make.bat' - return 'make' - - -@task -def run_tests(): - """Run all tests""" - sh('python3 %s' % path('tests/run_tests.py')) diff --git a/requirements-dev.txt b/requirements-dev.txt index d026ec6d..02ef3904 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,2 @@ flake8==5.0.4 -Paver==1.3.4 -pylint==2.13.9 +invoke==3.0.3 diff --git a/tasks.py b/tasks.py new file mode 100644 index 00000000..483a8f8b --- /dev/null +++ b/tasks.py @@ -0,0 +1,301 @@ +# ================================================================= +# +# Authors: Tom Kralidis +# +# Copyright (c) 2026 Tom Kralidis +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# ================================================================= + +import codecs +import glob +from io import BytesIO +import os +from pathlib import Path +import shutil +import tempfile +from urllib.request import urlopen +import zipfile + +from invoke import task + +BASEDIR = Path(__file__).resolve().parent +HOME = BASEDIR +DOCS = BASEDIR / 'docs' +INSTANCE = BASEDIR / 'instance' +POT = BASEDIR / 'GeoHealthCheck/translations/en/LC_MESSAGES/messages.po' +STATIC_DOCS = BASEDIR / 'GeoHealthCheck/static/docs' +STATIC_LIB = BASEDIR / 'GeoHealthCheck/static/lib' +TMP = Path(tempfile.mkdtemp()) +TRANSLATIONS = BASEDIR / 'GeoHealthCheck/translations' + + +@task +def setup(c): + """setup plugin dependencies""" + + config_file = HOME / 'GeoHealthCheck/config_main.py' + config_site = INSTANCE / 'config_site.py' + + # setup dirs + if not STATIC_LIB.exists(): + STATIC_LIB.mkdir() + if not INSTANCE.exists(): + INSTANCE.mkdir() + data_dir = INSTANCE / 'data' + data_dir.mkdir() + data_dir.chmod(0o777) + shutil.copy2(config_file, config_site) + + # setup deps + c.run('pip3 install -r requirements.txt') + + skin = 'http://github.com/BlackrockDigital/startbootstrap-sb-admin-2/archive/v3.3.7+1.zip' # noqa + + skin_dirs = ['dist', 'vendor'] + need_to_fetch = False + + for skin_dir in skin_dirs: + skin_dir_path = Path('startbootstrap-sb-admin-2-3.3.7-1') / skin_dir + if not skin_dir_path.exists(): + need_to_fetch = True + + if need_to_fetch: + zipstr = BytesIO(urlopen(skin).read()) + zipfile_obj = zipfile.ZipFile(zipstr) + zipfile_obj.extractall(STATIC_LIB) + + for zf_mem in skin_dirs: + src_loc = STATIC_LIB / 'startbootstrap-sb-admin-2-3.3.7-1' / zf_mem + dest_loc = STATIC_LIB / zf_mem + if not dest_loc.exists(): + shutil.move(src_loc, dest_loc) + else: + print('directory already exists. Skipping') + + shutil.rmtree(STATIC_LIB / 'startbootstrap-sb-admin-2-3.3.7-1') + + # install bootstrap-tagsinput to static/lib + print('Getting select2') + select2 = 'https://github.com/select2/select2/archive/4.0.3.zip' + + zipstr = BytesIO(urlopen(select2).read()) + zipfile_obj = zipfile.ZipFile(zipstr) + zipfile_obj.extractall(STATIC_LIB) + + dirname = list(STATIC_LIB.glob('select2-*'))[0] + dstdir = ''.join(dirname.name.rsplit('-', 1)[:-1]) + + try: + os.rename(dirname, dstdir) + except OSError: + shutil.rmtree(dstdir) + dirname.rename(dstdir) + + # install leafletjs to static/lib + print('Getting leaflet') + leaflet_cdn_url = 'https://leafletjs-cdn.s3.amazonaws.com/content/leaflet' + leafletjs = f'{leaflet_cdn_url}/v1.9.4/leaflet.zip' + + zipstr = BytesIO(urlopen(leafletjs).read()) + zipfile_obj = zipfile.ZipFile(zipstr) + zipfile_obj.extractall(STATIC_LIB / 'leaflet') + + # install html5shiv to static/lib + cdn_url = 'https://cdn.jsdelivr.net/npm' + shiv_filepath = STATIC_LIB / 'html5shiv.min.js' + with shiv_filepath.open('w') as f: + url = f'{cdn_url}/html5shiv.min.js@3.7.2/html5shiv.min.js' + content = urlopen(url).read().decode() + f.write(content) + + # install respond to static/lib + respond_filepath = STATIC_LIB / 'respond.min.js' + with respond_filepath.open('w') as f: + url = f'{cdn_url}/respond.min.js@1.4.2/respond.min.js' + content = urlopen(url).read().decode() + f.write(content) + + # build i18n .mo files + compile_translations(c) + + # build local docs + refresh_docs(c) + + # message user + print(f'GeoHealthCheck is now built. Edit settings in {config_site}') + print('before deploying the application. Alternatively, you can start a') + print('development instance with "python3 GeoHealthCheck/app.py"') + + +@task +def create_secret_key(c): + """create secret key for SECRET_KEY in instance/config_site.py""" + + secret_key = codecs.encode(os.urandom(24), 'hex').decode() + print(f'Secret key: {secret_key}') + print('Copy/paste this key to set the SECRET_KEY') + print('value in instance/config_site.py') + + +@task +def create(c, email, username, password): + """create database objects and superuser account""" + + args = '' + models_py = Path('GeoHealthCheck/models.py') + + if None not in [email, username, password]: + args = f'{username} {password} {email}' + + c.run(f'python3 {models_py} create {args}') + + +@task +def create_hash(c, password): + """Create hash, mainly for passwords""" + + from util import create_hash as create_hash2 + + token = create_hash2(password) + print('Copy/paste the entire token below for example to set password') + print(token) + + +@task +def upgrade(c): + """upgrade database if changed; be sure to backup first!""" + + print('Upgrading database...') + os.chdir(BASEDIR / 'GeoHealthCheck') + c.run('python3 manage.py db upgrade') + os.chdir(BASEDIR) + + +@task +def create_wsgi(c): + """create WSGI wrapper and Apache2 configuration""" + + wsgi_script = INSTANCE / 'GeoHealthCheck.wsgi' + + with wsgi_script.open('w') as fh: + fh.write('import sys\n') + fh.write('sys.path.insert(0, \'%s\')\n' % BASEDIR) + fh.write('from GeoHealthCheck.app import APP as application') + + wsgi_conf = INSTANCE / 'GeoHealthCheck.conf' + + with open(wsgi_conf, 'w') as fh: + ghc_wsgi = INSTANCE / 'GeoHealthCheck.wsgi' + fh.write(f'WSGIScriptAlias / {ghc_wsgi}\n') + fh.write(f'\n') + fh.write('Order deny,allow\n') + fh.write('Allow from all\n') + fh.write('') + + +@task +def refresh_docs(c): + """Build sphinx docs from scratch""" + + make = 'make' + + if os.name == 'nt': + make = 'make.bat' + + if STATIC_DOCS.exists(): + shutil.rmtree(STATIC_DOCS) + + os.chdir(DOCS) + c.run(f'{make} clean') + c.run(f'{make} html') + + source_html_dir = BASEDIR / 'docs/_build/html' + shutil.copytree(source_html_dir, STATIC_DOCS) + os.chdir(BASEDIR) + + +@task +def clean(c): + """clean environment""" + + for dir_ in [STATIC_LIB, STATIC_DOCS, TMP]: + if dir_.exists(): + shutil.rmtree(dir_) + + +@task +def extract_translations(c): + """extract translations wrapped in _() or gettext()""" + + pot_dir = Path('GeoHealthCheck/translations/en/LC_MESSAGES') + if not pot_dir.exists(): + pot_dir.mkdir(parants=True, exist_ok=True) + + c.run(f'pybabel extract -F babel.cfg -o {POT} GeoHealthCheck') + + +@task +def add_language_catalogue(c, lang): + """adds new language profile""" + + if lang is None: + raise RuntimeError('missing lang argument') + + c.run(f'pybabel init -i {POT} -d {TRANSLATIONS} -l {lang}') + + +@task +def compile_translations(c): + """build .mo files""" + + c.run(f'pybabel compile -d {TRANSLATIONS}') + + +@task +def update_translations(c): + """update language strings""" + + extract_translations(c) + c.run(f'pybabel update -i {POT} -d {TRANSLATIONS}') + + +@task +def runner_daemon(c): + """Run the HealthCheck runner daemon scheduler""" + + c.run('python3 GeoHealthCheck/scheduler.py') + + +@task +def run_healthchecks(c): + """Run all HealthChecks directly""" + + c.run('python3 GeoHealthCheck/healthcheck.py') + + +@task +def run_tests(c): + """Run all tests""" + + c.run('python3 tests/run_tests.py')