Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Introduce mkdocs for API documentation #31

Merged
merged 16 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
.idea
*.iml
.vscode
venv/*

# Folders created by setuptools.
build
dist
pyodk.egg-info
pip-wheel-metadata/*

# Pypi manifest.
MANIFEST
Expand Down
155 changes: 67 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@ An API client for the [ODK Central API](https://odkcentral.docs.apiary.io). Use

This library aims to make common data analysis and workflow automation tasks as simple as possible by providing clear method names, types, and examples. It also provides convenient access to the full API using [HTTP verb methods](#raw-http-requests).


# Install
## Install

The currently supported Python version for `pyodk` is 3.8.

### From pip

## From pip

```sh
```bash
pip install pyodk
```

### From source

## From source

```sh
```bash
# Get a copy of the repository.
mkdir -P ~/repos/pyodk
cd ~/repos/pyodk
Expand All @@ -38,33 +35,61 @@ pip install -e .
deactivate
```

## Configure

# Configuration


## Main configuration file
The configuration file uses the TOML format. The default file name is `.pyodk_config.toml`, and the default location is the user home directory. The file name and location can be customised by setting the environment variable `PYODK_CONFIG_FILE` to some other file path, or by passing the path at init with `Client(config_path="my_config.toml")`. The expected file structure is as follows:

The main configuration file uses the TOML format. The default file name is `.pyodk_config.toml`, and the default location is the user home directory. The file name and location can be customised by setting the environment variable `PYODK_CONFIG_FILE` to some other file path, or by passing the path at init with `Client(config_path="my_config.toml")`. The expected file structure is as follows:

```
```toml
[central]
base_url = "https://www.example.com"
username = "my_user"
password = "my_password"
default_project_id = 123
```

### Custom configuration file paths

The `Client` is specific to a configuration and cache file. These approximately correspond to the session which the `Client` represents; it also encourages segregating credentials. These paths can be set by:

- Setting environment variables `PYODK_CONFIG_FILE` and `PYODK_CACHE_FILE`
- Init arguments: `Client(config_path="my_config.toml", cache_path="my_cache.toml")`.

### Default project

The `Client` is not specific to a project, but a default `project_id` can be set by:

- A `default_project_id` in the configuration file.
- An init argument: `Client(project_id=1)`.
- A property on the client: `client.project_id = 1`.

## Session cache file
### Session cache file

The session cache file uses the TOML format. The default file name is `.pyodk_cache.toml`, and the default location is the user home directory. The file name and location can be customised by setting the environment variable `PYODK_CACHE_FILE` to some other file path, or by passing the path at init with `Client(config_path="my_cache.toml")`. This file should not be pre-created as it is used to store a session token after login.

## Use

To get started with `pyODK`, build a `Client`:

```python
from pyodk.client import Client

client = Client()
```

# Usage
Authentication is triggered by the first API call on the `Client`, or by explicitly using `Client.open()`.

Authentication is triggered by the first API call on the Client, or by using `Client.open()`. Use `Client.close()` to clean up a client session. Clean up is recommended for long-running scripts, e.g. analysis notebooks, web apps, etc.
Use `Client.close()` to clean up a client session. Clean up is recommended for long-running scripts, e.g. web apps, etc.

## Examples
You can also use the Client as a context manager to manage authentication and clean up:

```python
with Client() as client:
print(client.projects.list())
```

### Examples

**👉 See detailed tutorials in the Examples library in the pyODK documentation.**

```python
from pyodk.client import Client
Expand All @@ -83,85 +108,27 @@ client.forms.update(
client.close()
```

When using the Client as a context manager, authentication occurs at entry and clean up occurs at exit.

```python
with Client() as client:
print(client.projects.list())
```

**👉 Looking for more advanced examples? You can find detailed Jupyter notebooks, scripts, and webinars [here](examples).**

## Default project

The `Client` is not specific to a project, but a default `project_id` can be set by:

- A `default_project_id` in the configuration file.
- An init argument: `Client(project_id=1)`.
- A property on the client: `client.project_id = 1`.

## Custom configuration file paths

The `Client` is specific to a configuration and cache file. These approximately correspond to the session which the `Client` represents; it also encourages segregating credentials. These paths can be set by:

- Setting environment variables `PYODK_CONFIG_FILE` and `PYODK_CACHE_FILE`
- Init arguments: `Client(config_path="my_config.toml", cache_path="my_cache.toml")`.


## Methods

Available methods on `Client`:

- Projects
- list: Read all Project details.
- get: Read Project details.
- create_app_users: Create new project app users, and optionally assign forms to them.
- Forms
- list: Read all Form details.
- get: Read Form details.
- update: Create a new version of an existing Form.
- Submissions
- list: Read all Submission metadata.
- get: Read Submission metadata.
- get_table: Read Submission data.
- create: Create a Submission.
- edit: Edit a submission, and optionally comment on it.
- review: Update Submission metadata (review state), and optionally comment on it.
- list_comments: Read Comment data for a Submission.
- add_comment: Create a Comment for a Submission.

- *for additional requests*
- get
- post
- put
- patch
- delete

See issues for additions to `pyodk` that are under consideration. Please file new issues for any functionality you are missing.

## Raw HTTP requests
### Raw HTTP requests
For interacting with parts of the ODK Central API ([docs](https://odkcentral.docs.apiary.io)) that have not been implemented in `pyodk`, use HTTP verb methods exposed on the `Client`:

```
client.get("projects/8")
client.post("projects/7/app-users", json={"displayName": "Lab Tech"})
```
You can find a more detailed tutorial [in the examples](examples/).
You can find a more detailed tutorial in the Examples library in the pyODK documentation.

These methods provide convenient access to `Client.session`, which is a `requests.Session` object subclass. The `Session` has customised to prefix request URLs with the `base_url` from the pyodk config. For example with a base_url `https://www.example.com`, a call to `client.session.get("projects/8")` gets the details of `project_id=8`, using the full url `https://www.example.com/v1/projects/8`.

## Session customization
### Session customization
If Session behaviour needs to be customised, for example to set alternative timeouts or retry strategies, etc., then subclass the `pyodk.session.Session` and provide an instance to the `Client` constructor, e.g. `Client(session=my_session)`.


## Logging

Errors and other messages are logged to a standard library `logging` logger in the `pyodk` namespace / hierarchy (e.g `pyodk.config`, `pyodk.endpoints.auth`, etc.). The logs can be manipulated from an application as follows.
### Logging
Errors raised by pyODK and other messages are logged using the `logging` standard library. The logger is in the `pyodk` namespace / hierarchy (e.g `pyodk.config`, `pyodk.endpoints.auth`, etc.). The logs can be manipulated from your script / app as follows.

```python
import logging


# Initialise an example basic logging config (writes to stdout/stderr).
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
Expand All @@ -178,29 +145,41 @@ pyodk_log.setLevel(logging.FATAL)
pyodk_log.propagate = False
```

### Errors raised by pyODK
Error types raised by pyODK are found in `errors.py`, which currently is only the `PyODKError`. In general this error is raised when:

# Development
- The pyODK configuration is invalid (missing file, missing fields, etc).
- The client method arguments are invalid (missing, wrong type, etc.).
- The response from ODK Central indicated and error (HTTP >=400, <600).
- The data returned from ODK Central does not have the expected fields or types.

Note that pyODK does not attempt to wrap every possible error condition, so if needed, broader exception handling should be included in your script / app.

## Contribute

See issues for additions to `pyodk` that are under consideration. Please file new issues for any functionality you are missing.

## Develop

Install the source files as described above, then:

```sh
```bash
pip install -r dev_requirements.pip
```

You can run tests with:

```
```bash
nosetests
```

On Windows, use:

```
```bash
nosetests -v -v --traverse-namespace ./tests
```


# Releases
## Release

1. Run all linting and tests.
1. Draft a new GitHub release with the list of merged PRs.
Expand Down
2 changes: 1 addition & 1 deletion dev_requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ mock
pip-tools
flake8
isort
black
black
1 change: 1 addition & 0 deletions dev_requirements.pip
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ build==0.8.0 # via pip-tools
certifi==2022.6.15 # via requests
charset-normalizer==2.1.0 # via requests
click==8.1.3 # via black, pip-tools
colorama==0.4.6 # via build, click
flake8==5.0.1 # via -r dev_requirements.in
idna==3.3 # via requests
isort==5.10.1 # via -r dev_requirements.in
Expand Down
12 changes: 12 additions & 0 deletions docs/assets/extra_css.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@font-face {
font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}

:root {
--md-text-font: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'";
--md-accent-fg-color:#009ecc;
}

.md-content,.md-sidebar,.md-header {
--md-typeset-a-color:#009ecc;
}
Binary file added docs/assets/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions docs/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Client

::: pyodk.client.Client
5 changes: 5 additions & 0 deletions docs/docs_requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-e .
mkdocs
mkdocstrings
mkdocstrings-python
mkdocs-jupyter
70 changes: 70 additions & 0 deletions docs/docs_requirements.pip
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
#
# pip-compile --annotation-style=line --output-file=docs_requirements.pip docs_requirements.in
#
attrs==22.2.0 # via jsonschema
beautifulsoup4==4.11.2 # via nbconvert
bleach==6.0.0 # via nbconvert
certifi==2022.12.7 # via requests
charset-normalizer==2.1.1 # via requests
click==8.1.3 # via mkdocs
colorama==0.4.6 # via click, griffe, mkdocs
defusedxml==0.7.1 # via nbconvert
entrypoints==0.4 # via nbconvert
fastjsonschema==2.16.3 # via nbformat
ghp-import==2.1.0 # via mkdocs
griffe==0.25.5 # via mkdocstrings-python
idna==3.4 # via requests
importlib-metadata==6.0.0 # via jupyter-client, markdown, mkdocs
importlib-resources==5.12.0 # via jsonschema
jinja2==3.1.2 # via mkdocs, mkdocs-material, mkdocstrings, nbconvert
jsonschema==4.17.3 # via nbformat
jupyter-client==8.0.3 # via nbclient
jupyter-core==5.2.0 # via jupyter-client, nbclient, nbconvert, nbformat
jupyterlab-pygments==0.2.2 # via nbconvert
jupytext==1.14.5 # via mkdocs-jupyter
lxml==4.9.2 # via nbconvert
markdown==3.3.7 # via mkdocs, mkdocs-autorefs, mkdocs-material, mkdocstrings, pymdown-extensions
markdown-it-py==2.2.0 # via jupytext, mdit-py-plugins
markupsafe==2.1.2 # via jinja2, mkdocstrings, nbconvert
mdit-py-plugins==0.3.5 # via jupytext
mdurl==0.1.2 # via markdown-it-py
mergedeep==1.3.4 # via mkdocs
mistune==0.8.4 # via nbconvert
mkdocs==1.4.2 # via -r docs_requirements.in, mkdocs-autorefs, mkdocs-jupyter, mkdocs-material, mkdocstrings
mkdocs-autorefs==0.4.1 # via mkdocstrings
mkdocs-jupyter==0.22.0 # via -r docs_requirements.in
mkdocs-material==8.5.11 # via mkdocs-jupyter
mkdocs-material-extensions==1.1.1 # via mkdocs-material
mkdocstrings==0.20.0 # via -r docs_requirements.in, mkdocstrings-python
mkdocstrings-python==0.8.3 # via -r docs_requirements.in
nbclient==0.7.2 # via nbconvert
nbconvert==6.5.4 # via mkdocs-jupyter
nbformat==5.7.3 # via jupytext, nbclient, nbconvert
packaging==23.0 # via mkdocs, nbconvert
pandocfilters==1.5.0 # via nbconvert
pkgutil-resolve-name==1.3.10 # via jsonschema
platformdirs==3.1.0 # via jupyter-core
pydantic==1.10.1 # via pyodk
pygments==2.14.0 # via mkdocs-jupyter, mkdocs-material, nbconvert
pymdown-extensions==9.10 # via mkdocs-material, mkdocstrings
pyrsistent==0.19.3 # via jsonschema
python-dateutil==2.8.2 # via ghp-import, jupyter-client
pywin32==305 # via jupyter-core
pyyaml==6.0 # via jupytext, mkdocs, pymdown-extensions, pyyaml-env-tag
pyyaml-env-tag==0.1 # via mkdocs
pyzmq==25.0.0 # via jupyter-client
requests==2.28.1 # via mkdocs-material, pyodk
six==1.16.0 # via bleach, python-dateutil
soupsieve==2.4 # via beautifulsoup4
tinycss2==1.2.1 # via nbconvert
toml==0.10.2 # via jupytext, pyodk
tornado==6.2 # via jupyter-client
traitlets==5.9.0 # via jupyter-client, jupyter-core, nbclient, nbconvert, nbformat
typing-extensions==4.5.0 # via pydantic
urllib3==1.26.14 # via requests
watchdog==2.3.1 # via mkdocs
webencodings==0.5.1 # via bleach, tinycss2
zipp==3.15.0 # via importlib-metadata, importlib-resources
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions docs/forms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Forms

::: pyodk._endpoints.forms.FormService
12 changes: 12 additions & 0 deletions docs/http-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# HTTP verb methods

For interacting with parts of the ODK Central API ([docs](https://odkcentral.docs.apiary.io)) that have not been implemented in `pyodk`, use HTTP verb methods exposed on the `Client`:

```python
client.get("projects/8")
client.post("projects/7/app-users", json={"displayName": "Lab Tech"})
```

These methods provide convenient access to `Client.session`, which is a [`requests.Session`](https://requests.readthedocs.io/en/latest/user/advanced/#session-objects) object subclass. The `Session` has customised to prefix request URLs with the `base_url` from the pyodk config. For example with a base_url `https://www.example.com`, a call to `client.session.get("projects/8")` gets the details of `project_id=8`, using the full url `https://www.example.com/v1/projects/8`.

Learn more in [this example](/examples/beyond-library-methods/).
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "README.md"
3 changes: 3 additions & 0 deletions docs/projects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Projects

::: pyodk._endpoints.projects.ProjectService
Loading