Skip to content

Commit acc8938

Browse files
authored
Merge pull request #25 from man-group/database-plugins
#13 Support database plugins / rework of entrypoints
2 parents 119b48e + fdcf945 commit acc8938

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+872
-842
lines changed

.circleci/config.yml

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defaults: &defaults
55
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
66
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
77
# CODECOV_TOKEN: b0d35139-0a75-427a-907b-2c78a762f8f0
8-
VERSION: 0.0.2
8+
VERSION: 0.1.0
99
PANDOC_RELEASES_URL: https://github.com/jgm/pandoc/releases
1010
YARN_STATIC_DIR: notebooker/web/static/
1111
IMAGE_NAME: mangroup/notebooker
@@ -25,25 +25,38 @@ defaults: &defaults
2525
name: Restore Yarn Package Cache
2626
keys:
2727
- yarn-packages-{{ checksum "notebooker/web/static/yarn.lock" }}
28+
- run:
29+
name: Version checks
30+
command: |
31+
grep -q $VERSION notebooker/_version.py || (echo "ERROR: Version number not found in notebooker/_version.py: $VERSION"; exit 1)
32+
grep -q $VERSION CHANGELOG.md || (echo "ERROR: Version number not found in CHANGES.md: $VERSION"; exit 1)
33+
grep -q $VERSION docs/conf.py || (echo "ERROR: Version number not found in docs/source/conf.py: $VERSION"; exit 1)
34+
grep -q $VERSION notebooker/web/static/package.json || (echo "ERROR: Version number not found in package.json: $VERSION"; exit 1)
35+
- run:
36+
name: Install MongoDB
37+
command: |
38+
# run "cat /etc/os-release" to view information about the OS
39+
# good article on how to install mongo, https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
40+
41+
cat /etc/os-release
42+
set -x
43+
wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -
44+
sudo apt-get install gnupg
45+
wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -
46+
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list
47+
sudo apt-get update
48+
sudo ln -s /bin/true /bin/systemctl
49+
sudo apt-get install -y mongodb-org=4.2.11 mongodb-org-server=4.2.11 mongodb-org-shell=4.2.11 mongodb-org-mongos=4.2.11 mongodb-org-tools=4.2.11
2850
- run:
2951
name: Install JS Dependencies
3052
command: |
31-
pushd $YARN_STATIC_DIR
32-
yarn install --frozen-lockfile
53+
pushd $YARN_STATIC_DIR
54+
yarn install --frozen-lockfile
3355
- save_cache:
3456
name: Save Yarn Package Cache
3557
key: yarn-packages-{{ checksum "notebooker/web/static/yarn.lock" }}
3658
paths:
37-
- ~/.cache/yarn
38-
- run:
39-
name: Install MongoDB
40-
command: |
41-
# run "cat /etc/os-release" to view information about the OS
42-
# this article really helped with this madness: https://linuxize.com/post/how-to-install-mongodb-on-debian-9/
43-
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
44-
echo "deb http://repo.mongodb.org/apt/debian stretch/mongodb-org/4.0 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
45-
sudo apt-get update
46-
sudo apt-get install -y mongodb-org
59+
- ~/.cache/yarn
4760
- run:
4861
name: Lint & Format JS Code
4962
command: |
@@ -86,12 +99,15 @@ defaults: &defaults
8699
- run:
87100
name: Run all tests
88101
command: |
102+
set -x
89103
. ci/bin/activate
104+
ls -la /bin | grep mongo
105+
which mongod
90106
pip install -e .[prometheus,test]
91107
python -m ipykernel install --user --name=notebooker_kernel
92108
pip install -r ./notebooker/notebook_templates_example/notebook_requirements.txt
93109
mkdir test-results
94-
pytest --junitxml=test-results/junit.xml
110+
py.test -svvvvv --junitxml=test-results/junit.xml
95111
# bash <(curl -s https://codecov.io/bash) -c -F python
96112
- run:
97113
name: Build Sphinx Documentation
@@ -109,10 +125,6 @@ defaults: &defaults
109125
. ci/bin/activate
110126
pip install docutils
111127
pip install Pygments
112-
grep -q $VERSION notebooker/_version.py || (echo "ERROR: Version number not found in notebooker/_version.py: $VERSION"; exit 1)
113-
grep -q $VERSION CHANGELOG.md || (echo "ERROR: Version number not found in CHANGES.md: $VERSION"; exit 1)
114-
grep -q $VERSION docs/conf.py || (echo "ERROR: Version number not found in docs/source/conf.py: $VERSION"; exit 1)
115-
grep -q $VERSION notebooker/web/static/package.json || (echo "ERROR: Version number not found in package.json: $VERSION"; exit 1)
116128
python setup.py --long-description > ../README.rst
117129
cat ../README.rst | rst2html.py 1> ../README.html 2> ../log
118130
cp ../README.rst /tmp/circleci-artifacts
@@ -149,13 +161,19 @@ defaults: &defaults
149161
path: test-results
150162
version: 2
151163
jobs:
152-
build:
153-
working_directory: ~/notebooker
164+
build_3_6:
165+
working_directory: ~/notebooker_3_6
166+
docker:
167+
- image: cimg/python:3.6-node
168+
<<: *defaults
169+
build_3_7:
170+
working_directory: ~/notebooker_3_7
154171
docker:
155-
- image: circleci/python:3.6-stretch-node-browsers
172+
- image: cimg/python:3.7-node
156173
<<: *defaults
157174
workflows:
158175
version: 2
159176
build_all:
160177
jobs:
161-
- build
178+
- build_3_6
179+
- build_3_7

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
0.1.0 (2020-11-30)
2+
------------------
3+
Support for database plugins and tidying up configuration to be consistent across the board.
4+
5+
**Breaking changes**
6+
* 3 primary entrypoints have been consolidated under one - notebooker-cli, e.g. `notebooker-cli start-webapp` and `notebooker-cli execute-notebook`. Run notebooker-cli --help for more info.
7+
* In config, PY_TEMPLATE_DIR has been renamed to PY_TEMPLATE_BASE_DIR
8+
* In config, GIT_REPO_TEMPLATE_DIR has been renamed to PY_TEMPLATE_SUBDIR
9+
110
0.0.2 (2020-10-25)
211
------------------
312
Bugfixes & cleanup

docker/docker-compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ services:
1717
MONGO_HOST: mongodb:27017
1818
# this should be something like "notebooker" but this simplifies the compose file
1919
DATABASE_NAME: admin
20-
RESULT_COLLECTION_NAME: notebook_results
20+
RESULT_COLLECTION_NAME: NOTEBOOK_OUTPUT
2121

22-
PY_TEMPLATE_DIR: /var/run/template_repo
22+
PY_TEMPLATE_BASE_DIR: /var/run/template_repo
2323
volumes:
2424
- git-repo:/var/run/template_repo
2525
command: ["notebooker_webapp"]

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
author = "Man Group Quant Tech"
2424

2525
# The full version, including alpha/beta/rc tags
26-
release = "0.0.2"
26+
release = "0.1.0"
2727

2828

2929
# -- General configuration ---------------------------------------------------

docs/report_execution.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Executing a Notebook
1919
There are two primary ways to do this: either through the webapp or through the entrypoint. Both
2020
of these methods will rely on a `notebooker_kernel` being available in the current ipykernel environment.
2121

22+
For more information on the entrypoint, please run: `notebooker-cli execute-notebook --help`
23+
2224
Technologies
2325
------------
2426
Notebooker leverages multiple open-source technologies but in particular, it heavily makes use of some

docs/setup.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ NB: mongo should be running as above for these steps to work!
6060

6161
.. code:: bash
6262
63-
$ MONGO_HOST=localhost:27017 MONGO_USER=jon MONGO_PASSWORD=hello PORT=11828 notebooker_webapp
63+
$ notebooker-cli --mongo-host localhost:27017 --mongo-user jon --mongo-password hello start-webapp --port 11828
6464
6565
4. Open the link that is printed in your web browser.
6666

@@ -117,7 +117,7 @@ NB: mongo should be running as above for these steps to work!
117117

118118
.. code:: bash
119119
120-
$ MONGO_HOST=localhost:27017 MONGO_USER=jon MONGO_PASSWORD=hello PORT=11828 notebooker_webapp
120+
$ notebooker-cli --mongo-host localhost:27017 --mongo-user jon --mongo-password hello start-webapp --port 11828
121121
122122
123123
7. Open the link that is printed in your web browser.

docs/templates.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ installed, should be added to that folder.
2525

2626
For Notebooker to use a your checked-out repository, set two environment variables:
2727

28-
* Set :code:`PY_TEMPLATE_DIR` to the checked-out repository
29-
* Set :code:`GIT_REPO_TEMPLATE_DIR` to the subdirectory within your git repo which contains the templates
28+
* Set :code:`PY_TEMPLATE_BASE_DIR` to the checked-out repository
29+
* Set :code:`PY_TEMPLATE_SUBDIR` to the subdirectory within your git repo which contains the templates
3030

3131
Adding parameters
3232
-----------------

notebooker/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
from ._version import __version__
2+
3+
__import__("pkg_resources").declare_namespace(__name__)

notebooker/_entrypoints.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import os
2+
import uuid
3+
4+
import click
5+
6+
from notebooker.constants import DEFAULT_SERIALIZER
7+
from notebooker.execute_notebook import execute_notebook_entrypoint
8+
from notebooker.serialization import SERIALIZER_TO_CLI_OPTIONS
9+
from notebooker.settings import BaseConfig, WebappConfig
10+
from notebooker.snapshot import snap_latest_successful_notebooks
11+
from notebooker.web.app import main
12+
13+
14+
class NotebookerEntrypoint(click.Group):
15+
def parse_args(self, ctx, args):
16+
try:
17+
serializer_arg = args.index("--serializer-cls")
18+
serializer = args[serializer_arg + 1]
19+
except ValueError:
20+
serializer = DEFAULT_SERIALIZER
21+
self.params += SERIALIZER_TO_CLI_OPTIONS[serializer].params
22+
23+
return super().parse_args(ctx, args)
24+
25+
26+
pass_config = click.make_pass_decorator(BaseConfig)
27+
28+
29+
def filesystem_default_value(dirname):
30+
return os.path.join(os.path.expanduser("~"), ".notebooker", dirname, str(uuid.uuid4()))
31+
32+
33+
@click.group(cls=NotebookerEntrypoint)
34+
@click.option("--notebook-kernel-name", default=None, help="The name of the kernel which is running our notebook code.")
35+
@click.option(
36+
"--output-base-dir",
37+
default=filesystem_default_value("output"),
38+
help="The base directory to which we will save our notebook output temporarily. Required by Papermill.",
39+
)
40+
@click.option(
41+
"--template-base-dir",
42+
default=filesystem_default_value("templates"),
43+
help="The base directory to which we will save our notebook templates which have been converted "
44+
"from .py to .ipynb.",
45+
)
46+
@click.option(
47+
"--py-template-base-dir",
48+
default=None,
49+
help="The base directory of the git repository which holds the notebook templates as .py files. "
50+
"If not specified, this will default to the sample directory within notebooker.",
51+
)
52+
@click.option(
53+
"--py-template-subdir",
54+
default=None,
55+
help="The subdirectory of the git repository which contains only notebook templates.",
56+
)
57+
@click.option(
58+
"--notebooker-disable-git",
59+
default=False,
60+
is_flag=True,
61+
help="If selected, notebooker will not try to pull the latest version of python templates from git.",
62+
)
63+
@click.option(
64+
"--serializer-cls",
65+
default=DEFAULT_SERIALIZER,
66+
help="The serializer class through which we will save the notebook result.",
67+
)
68+
@click.pass_context
69+
def base_notebooker(
70+
ctx,
71+
notebook_kernel_name,
72+
output_base_dir,
73+
template_base_dir,
74+
py_template_base_dir,
75+
py_template_subdir,
76+
notebooker_disable_git,
77+
serializer_cls,
78+
**serializer_args,
79+
):
80+
config = BaseConfig(
81+
SERIALIZER_CLS=serializer_cls,
82+
SERIALIZER_CONFIG=serializer_args,
83+
NOTEBOOK_KERNEL_NAME=notebook_kernel_name,
84+
OUTPUT_DIR=output_base_dir,
85+
TEMPLATE_DIR=template_base_dir,
86+
PY_TEMPLATE_BASE_DIR=py_template_base_dir,
87+
PY_TEMPLATE_SUBDIR=py_template_subdir,
88+
NOTEBOOKER_DISABLE_GIT=notebooker_disable_git,
89+
)
90+
ctx.obj = config
91+
92+
93+
@base_notebooker.command()
94+
@click.option("--port", default=11828)
95+
@click.option("--logging-level", default="INFO")
96+
@click.option("--debug", default=False)
97+
@click.option("--base-cache-dir", default=filesystem_default_value("webcache"))
98+
@pass_config
99+
def start_webapp(config: BaseConfig, port, logging_level, debug, base_cache_dir):
100+
web_config = WebappConfig.copy_existing(config)
101+
web_config.PORT = port
102+
web_config.LOGGING_LEVEL = logging_level
103+
web_config.DEBUG = debug
104+
web_config.CACHE_DIR = base_cache_dir
105+
return main(web_config)
106+
107+
108+
@base_notebooker.command()
109+
@click.option("--report-name", help="The name of the template to execute, relative to the template directory.")
110+
@click.option(
111+
"--overrides-as-json", default="{}", help="The parameters to inject into the notebook template, in JSON format."
112+
)
113+
@click.option(
114+
"--iterate-override-values-of",
115+
default="",
116+
help="For the key/values in the overrides, set this to the value of one of the keys to run reports for "
117+
"each of its values.",
118+
)
119+
@click.option("--report-title", default="", help="A custom title for this notebook. The default is the report_name.")
120+
@click.option("--n-retries", default=3, help="The number of times to retry when executing this notebook.")
121+
@click.option(
122+
"--job-id",
123+
default=str(uuid.uuid4()),
124+
help="The unique job ID for this notebook. Can be non-unique, but note that you will overwrite history.",
125+
)
126+
@click.option("--mailto", default="", help="A comma-separated list of email addresses which will receive results.")
127+
@click.option("--pdf-output/--no-pdf-output", default=True, help="Whether we generate PDF output or not.")
128+
@click.option(
129+
"--prepare-notebook-only",
130+
is_flag=True,
131+
help='Used for debugging and testing. Whether to actually execute the notebook or just "prepare" it.',
132+
)
133+
@pass_config
134+
def execute_notebook(
135+
config: BaseConfig,
136+
report_name,
137+
overrides_as_json,
138+
iterate_override_values_of,
139+
report_title,
140+
n_retries,
141+
job_id,
142+
mailto,
143+
pdf_output,
144+
prepare_notebook_only,
145+
):
146+
if report_name is None:
147+
raise ValueError("Error! Please provide a --report-name.")
148+
return execute_notebook_entrypoint(
149+
config,
150+
report_name,
151+
overrides_as_json,
152+
iterate_override_values_of,
153+
report_title,
154+
n_retries,
155+
job_id,
156+
mailto,
157+
pdf_output,
158+
prepare_notebook_only,
159+
)
160+
161+
162+
@base_notebooker.command()
163+
@click.option(
164+
"--report-name", required=True, help="The name of the template to retrieve, relative to the template directory."
165+
)
166+
@pass_config
167+
def snapshot_latest_successful_notebooks(config: BaseConfig, report_name):
168+
snap_latest_successful_notebooks(config, report_name)
169+
170+
171+
if __name__ == "__main__":
172+
base_notebooker()

notebooker/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.2"
1+
__version__ = "0.1.0"

0 commit comments

Comments
 (0)