diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..b070078 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,48 @@ +name: Deploy docs + +on: + push: + branches: [master, main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: pip install -r docs/requirements.txt + + - name: Build docs + run: mkdocs build --strict + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 5270e2f..720d4f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PyODX -For the latest documentation visit: https://pyodx.readthedocs.io +For the latest documentation visit: https://pyodx.webodm.org The information below is for managing the repository. @@ -21,17 +21,13 @@ docker run -ti -p 3000:3000 webodm/nodeodx --test Make sure you are using Python 3. ```bash -pip install virtualenv -virtualenv -p venv -source venv/bin/activate -pip install -r requirements.txt +pip install -r docs/requirements.txt ``` -Use [`sphinx-autobuild`](https://github.com/GaretJax/sphinx-autobuild) to automatically watch for changes and rebuild the html site using: +Serve the docs locally with live reload: -``` -cd docs -make livehtml +```bash +mkdocs serve ``` To stop the server press `Ctrl+C`. diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..3aa3776 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +pyodx.webodm.org diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index e49c97c..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -livehtml: - sphinx-autobuild -b html $(ALLSPHINXOPTS) "$(SOURCEDIR)" "$(BUILDDIR)" - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index b7a5e5c..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,186 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys -sys.path.insert(0, os.path.abspath('..')) - - -# -- Project information ----------------------------------------------------- - -project = 'PyODX' -copyright = '2026, WebODM Contributors' -author = 'WebODM Contributors' - -# The short X.Y version -version = '' -# The full version, including alpha/beta/rc tags -release = '' - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.napoleon', -] - -autodoc_mock_imports = [ - 'requests', - 'requests_toolbelt', - 'urllib3', - 'simplejson', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'PyODXdoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'PyODX.tex', 'PyODX Documentation', - 'WebODM Contributors', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pyodx', 'PyODX Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'PyODX', 'PyODX Documentation', - author, 'PyODX', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - - -# -- Extension configuration ------------------------------------------------- diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..53a793d --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,95 @@ +# Examples + +## Get Node Info + +Connect to a node and retrieve its information: + +```python +from pyodx import Node, exceptions + +node = Node.from_url("http://localhost:3000?token=abc") + +try: + print(node.info()) +except exceptions.NodeConnectionError as e: + print("Cannot connect: " + str(e)) +``` + +## Create a Task + +Upload images, process them, and download results: + +```python +import os +from pyodx import Node, exceptions + +node = Node("localhost", 3000) + +try: + # Start a task + print("Uploading images...") + task = node.create_task( + ["images/image_1.jpg", "images/image_2.jpg"], + {"dsm": True, "orthophoto-resolution": 4}, + ) + print(task.info()) + + try: + # Block until the task is finished + task.wait_for_completion() + + print("Task completed, downloading results...") + task.download_assets("./results") + print("Assets saved in ./results (%s)" % os.listdir("./results")) + + # Restart task and this time compute dtm + task.restart({"dtm": True}) + task.wait_for_completion() + + print("Task completed, downloading results...") + task.download_assets("./results_with_dtm") + print("Assets saved in ./results_with_dtm (%s)" % os.listdir("./results_with_dtm")) + + except exceptions.TaskFailedError as e: + print("\n".join(task.output())) + +except exceptions.NodeConnectionError as e: + print("Cannot connect: %s" % e) +except exceptions.NodeResponseError as e: + print("Error: %s" % e) +``` + +## Upload Progress Callback + +Track upload progress with a callback: + +```python +from pyodx import Node + +node = Node("localhost", 3000) + +def progress(percent): + print("Upload: %.2f%%" % percent) + +task = node.create_task( + ["image_1.jpg", "image_2.jpg"], + progress_callback=progress, +) +``` + +## Task Status Callback + +Monitor task status while waiting for completion: + +```python +from pyodx import Node + +node = Node("localhost", 3000) + +task = node.create_task(["image_1.jpg", "image_2.jpg"]) + +def status_update(info): + print("Status: %s, Progress: %.1f%%" % (info.status, info.progress)) + +task.wait_for_completion(status_callback=status_update) +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..72d148e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,47 @@ +# PyODX + +**A Python SDK for creating orthophotos, DEMs, 3D models and point clouds from aerial images via the [NodeODX API](https://github.com/WebODM/NodeODX/blob/master/docs/index.adoc).** + +PyODX is an official [WebODM](https://webodm.org) project. + +## Quick Start + +To test these examples start a [NodeODX](https://github.com/WebODM/NodeODX) node via: + +``docker run -ti --rm -p 3000:3000 webodm/nodeodx`` + +Then: + +```bash +pip install pyodx +``` + +```python +from pyodx import Node + +node = Node("localhost", 3000) + +task = node.create_task( + ["images/image_1.jpg", "images/image_2.jpg"], + {"dsm": True} +) +task.wait_for_completion() +task.download_assets("./results") +``` + +## Features + +- Create processing tasks from aerial images +- Monitor task progress with callbacks +- Download orthophotos, DEMs, point clouds and 3D models +- Parallel uploads and downloads +- Webhook support for task completion notifications + +## Requirements + +- Python 3.6+ + +## Next Steps + +- [Examples](examples.md) — full code examples +- [API Reference](reference.md) — complete API documentation diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index e63f0f5..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -Welcome to PyODX's documentation! -================================= - -.. automodule:: pyodx - :members: - -.. automodule:: pyodx.api - :members: - -.. automodule:: pyodx.types - :members: - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..7f7798f --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,36 @@ +# Installation + +## From PyPI + +```bash +pip install -U pyodx +``` + +## From Source + +```bash +git clone https://github.com/WebODM/PyODX.git +cd PyODX +pip install -e . +``` + +## Running a NodeODX Instance + +PyODX requires a running [NodeODX](https://github.com/WebODM/NodeODX) instance to process images. The easiest way to get one running is with Docker: + +```bash +docker run -ti --rm -p 3000:3000 webodm/nodeodx +``` + +This will start a NodeODX instance on `localhost:3000`. + +## Verifying the Installation + +```python +from pyodx import Node + +node = Node("localhost", 3000) +print(node.info()) +``` + +If the node is running, this will print information about the NodeODX instance including its version, available memory, and CPU cores. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 27f573b..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 0000000..8ac7672 --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,19 @@ +# Migrate from PyODM + +[PyODM](https://pypi.org/project/pyodm/) continues to work fine for most use cases, but will no longer receive updates. + +For new code, simply change your imports from pyodm to pyodx: + +```python +# from pyodm import Node +from pyodx import Node +``` + +We also renamed `OdmError` to `GenericError`: + +```python +# from pyodm.exceptions import OdmError +from pyodm.exceptions import GenericError +``` + +There are no other changes. \ No newline at end of file diff --git a/docs/reference.md b/docs/reference.md new file mode 100644 index 0000000..1504d80 --- /dev/null +++ b/docs/reference.md @@ -0,0 +1,53 @@ +# API Reference + +## Node + +::: pyodx.api.Node + options: + members: + - __init__ + - from_url + - info + - options + - version_greater_or_equal_than + - create_task + - get_task + - url + +## Task + +::: pyodx.api.Task + options: + members: + - info + - output + - cancel + - remove + - restart + - download_zip + - download_assets + - wait_for_completion + +## Types + +::: pyodx.types.NodeInfo + +::: pyodx.types.NodeOption + +::: pyodx.types.TaskInfo + +::: pyodx.types.TaskStatus + +## Exceptions + +::: pyodx.exceptions.GenericError + +::: pyodx.exceptions.NodeServerError + +::: pyodx.exceptions.NodeConnectionError + +::: pyodx.exceptions.NodeResponseError + +::: pyodx.exceptions.TaskFailedError + +::: pyodx.exceptions.RangeNotAvailableError diff --git a/docs/requirements.txt b/docs/requirements.txt index 86589e6..654e0eb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,2 @@ -Sphinx==8.0.2 -sphinx-autobuild==2024.9.3 +mkdocs-material==9.6.12 +mkdocstrings[python]==0.29.1 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a1d0b36 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,63 @@ +site_name: PyODX +site_url: https://pyodx.webodm.org +site_description: A Python SDK for NodeODX API +repo_url: https://github.com/WebODM/PyODX +repo_name: WebODM/PyODX + +theme: + name: material + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: blue + accent: light blue + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: blue + accent: light blue + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - content.code.copy + - navigation.instant + - navigation.sections + - navigation.top + - search.highlight + - search.suggest + +nav: + - Home: index.md + - Installation: installation.md + - Examples: examples.md + - API Reference: reference.md + - Migrate From PyODM: migration.md + +plugins: + - search + - mkdocstrings: + handlers: + python: + options: + show_source: true + show_root_heading: true + members_order: source + docstring_style: google + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - toc: + permalink: true + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/WebODM/PyODX diff --git a/pyodx/api.py b/pyodx/api.py index 717295e..e3fc21a 100644 --- a/pyodx/api.py +++ b/pyodx/api.py @@ -41,8 +41,8 @@ class Node: Args: host (str): Hostname or IP address of processing node port (int): Port of processing node - token (str): token to use for authentication - timeout (int): timeout value in seconds for network requests + token (str): Token to use for authentication + timeout (int): Timeout value in seconds for network requests """ prefixHttp = re.compile('http:', re.I) prefixHttps = re.compile('https:', re.I) @@ -59,14 +59,15 @@ def __init__(self, host, port, token="", timeout=30): def from_url(url, timeout=30): """Create a Node instance from a URL. - >>> n = Node.from_url("http://localhost:3000?token=abc") - Args: url (str): URL in the format proto://hostname:port/?token=value - timeout (int): timeout value in seconds for network requests + timeout (int): Timeout value in seconds for network requests Returns: - :func:`~Node` + Node (Node): Node instance + + Examples: + >>> n = Node.from_url("http://localhost:3000?token=abc") """ u = urlparse(url) qs = parse_qs(u.query) @@ -111,11 +112,11 @@ def url(self, url, query={}): """Get a URL relative to this node. Args: - url (str): relative URL - query (dict): query values to append to the URL + url (str): Relative URL + query (dict): Query values to append to the URL Returns: - str: Absolute URL + URL (str): Absolute URL """ netloc = self.host if (self.port == 80 or self.port == 443) else "{}:{}".format(self.host, self.port) proto = 'https' if self.port == 443 else 'http' @@ -170,26 +171,28 @@ def post(self, url, data=None, headers={}): def info(self): """Retrieve information about this node. - >>> n = Node('localhost', 3000) - >>> n.info().version - '2.3.1' - >>> n.info().engine - 'odx' - Returns: - :func:`~pyodx.types.NodeInfo` + NodeInfo (NodeInfo): Node information + + Examples: + >>> n = Node('localhost', 3000) + >>> n.info().version + '2.3.1' + >>> n.info().engine + 'odx' """ return NodeInfo(self.get('/info')) def options(self): """Retrieve the options available for creating new tasks on this node. - >>> n = Node('localhost', 3000) - >>> n.options()[0].name - 'end-with' - Returns: - list: [:func:`~pyodx.types.NodeOption`] + Options (list[NodeOption]): Available options + + Examples: + >>> n = Node('localhost', 3000) + >>> n.options()[0].name + 'end-with' """ return list(map(lambda o: NodeOption(**o), self.get('/options'))) @@ -197,17 +200,18 @@ def version_greater_or_equal_than(self, version): """Checks whether this node version is greater than or equal than a certain version number. - >>> n = Node('localhost', 3000) - >>> n.version_greater_or_equal_than('1.3.1') - True - >>> n.version_greater_or_equal_than('10.5.1') - False - Args: - version (str): version number to compare - + version (str): Version number to compare + Returns: - bool: result of comparison. + result (bool): True if node's version if >= version + + Examples: + >>> n = Node('localhost', 3000) + >>> n.version_greater_or_equal_than('1.3.1') + True + >>> n.version_greater_or_equal_than('10.5.1') + False """ node_version = self.info().version @@ -218,33 +222,35 @@ def create_task(self, files, options={}, name=None, progress_callback=None, skip """Start processing a new task. At a minimum you need to pass a list of image paths. All other parameters are optional. - >>> n = Node('localhost', 3000) - >>> t = n.create_task(['examples/images/image_1.jpg', 'examples/images/image_2.jpg'], \ - {'orthophoto-resolution': 2, 'dsm': True}) - >>> info = t.info() - >>> info.status - - >>> info.last_error - '' - >>> t.info().images_count - 2 - >>> t.output()[0:2] - ['DJI_0131.JPG - DJI_0313.JPG has 1 candidate matches', 'DJI_0131.JPG - DJI_0177.JPG has 3 candidate matches'] - Args: - files (list): list of image paths + optional GCP file path. - options (dict): options to use, for example {'orthophoto-resolution': 3, ...} - name (str): name for the task - progress_callback (function): callback reporting upload progress percentage + files (list): List of image paths + optional GCP file path. + options (dict): Options to use, for example {'orthophoto-resolution': 3, ...} + name (str): Name for the task + progress_callback (function): Callback reporting upload progress percentage skip_post_processing (bool): When true, skips generation of map tiles, derivate assets, point cloud tiles. webhook (str): Optional URL to call when processing has ended (either successfully or unsuccessfully). outputs (list): Optional paths relative to the project directory that should be included in the all.zip result file, overriding the default behavior. parallel_uploads (int): Number of parallel uploads. max_retries (int): Number of attempts to make before giving up on a file upload. retry_timeout (int): Wait at least these many seconds before attempting to upload a file a second time, multiplied by the retry number. - task_uuid: an optional UUID string that will be used as UUID for this task instead of generating a random one. + task_uuid (str): An optional UUID string that will be used as UUID for this task instead of generating a random one. + Returns: - :func:`~Task` + Task (Task): The created task + + Examples: + >>> n = Node('localhost', 3000) + >>> t = n.create_task(['examples/images/image_1.jpg', 'examples/images/image_2.jpg'], + ... {'orthophoto-resolution': 2, 'dsm': True}) + >>> info = t.info() + >>> info.status + + >>> info.last_error + '' + >>> t.info().images_count + 2 + >>> t.output()[0:2] + ['DJI_0131.JPG - DJI_0313.JPG has 1 candidate matches', 'DJI_0131.JPG - DJI_0177.JPG has 3 candidate matches'] """ if not self.version_greater_or_equal_than("1.4.0"): return self.create_task_fallback(files, options, name, progress_callback, task_uuid) @@ -455,21 +461,25 @@ def handle_task_new_response(self, result): def get_task(self, uuid): """Helper method to initialize a task from an existing UUID - >>> n = Node("localhost", 3000) - >>> t = n.get_task('00000000-0000-0000-0000-000000000000') - >>> t.__class__ - - Args: - uuid: Unique identifier of the task + uuid (str): Unique identifier of the task + + Returns: + Task (Task): The task instance + + Examples: + >>> n = Node("localhost", 3000) + >>> t = n.get_task('00000000-0000-0000-0000-000000000000') + >>> t.__class__ + """ return Task(self, uuid) class Task: - """A task is created to process images. To create a task, use :func:`~Node.create_task`. + """A task is created to process images. To create a task, use `Node.create_task`. Args: - node (:func:`~Node`): node this task belongs to + node (Node): Node this task belongs to uuid (str): Unique identifier assigned to this task. """ @@ -494,7 +504,7 @@ def info(self, with_output=None): """Retrieves information about this task. Returns: - :func:`~pyodx.types.TaskInfo` + TaskInfo (TaskInfo): Task information """ query = {} if with_output is not None: @@ -509,7 +519,7 @@ def output(self, line=0): line (int): Optional line number that the console output should be truncated from. For example, passing a value of 100 will retrieve the console output starting from line 100. Negative numbers are also allowed. For example -50 will retrieve the last 50 lines of console output. Defaults to 0 (retrieve all console output). Returns: - [str]: console output (one list item per row). + output (list[str]): Console output (one list item per row). """ return self.get('/task/{}/output'.format(self.uuid), {'line': line}) @@ -517,7 +527,7 @@ def cancel(self): """Cancel this task. Returns: - bool: task was canceled or not + success (bool): Task was canceled or not """ return self.post('/task/cancel', {'uuid': self.uuid}).get('success', False) @@ -525,7 +535,7 @@ def remove(self): """Remove this task. Returns: - bool: task was removed or not + success (bool): Task was removed or not """ return self.post('/task/remove', {'uuid': self.uuid}).get('success', False) @@ -533,10 +543,10 @@ def restart(self, options=None): """Restart this task. Args: - options (dict): options to use, for example {'orthophoto-resolution': 3, ...} + options (dict): Options to use, for example {'orthophoto-resolution': 3, ...} Returns: - bool: task was restarted or not + success (bool): Task was restarted or not """ data = {'uuid': self.uuid} if options is not None: data['options'] = options_to_json(options) @@ -546,12 +556,12 @@ def download_zip(self, destination, progress_callback=None, parallel_downloads=1 """Download this task's assets archive to a directory. Args: - destination (str): directory where to download assets archive. If the directory does not exist, it will be created. - progress_callback (function): an optional callback with one parameter, the download progress percentage. - parallel_downloads (int): maximum number of parallel downloads if the node supports http range. - parallel_chunks_size (int): size in MB of chunks for parallel downloads + destination (str): Directory where to download assets archive. If the directory does not exist, it will be created. + progress_callback (function): Optional callback with one parameter, the download progress percentage. + parallel_downloads (int): Maximum number of parallel downloads if the node supports http range. + parallel_chunks_size (int): Size in MB of chunks for parallel downloads Returns: - str: path to archive file (.zip) + Path (str): Path to archive file (.zip) """ info = self.info() if info.status != TaskStatus.COMPLETED: @@ -699,12 +709,12 @@ def download_assets(self, destination, progress_callback=None, parallel_download """Download this task's assets to a directory. Args: - destination (str): directory where to download assets. If the directory does not exist, it will be created. - progress_callback (function): an optional callback with one parameter, the download progress percentage - parallel_downloads (int): maximum number of parallel downloads if the node supports http range. - parallel_chunks_size (int): size in MB of chunks for parallel downloads + destination (str): Directory where to download assets. If the directory does not exist, it will be created. + progress_callback (function): Optional callback with one parameter, the download progress percentage + parallel_downloads (int): Maximum number of parallel downloads if the node supports http range. + parallel_chunks_size (int): Size in MB of chunks for parallel downloads Returns: - str: path to saved assets + Path (str): Path to saved assets """ zip_path = self.download_zip(destination, progress_callback=progress_callback, parallel_downloads=parallel_downloads, parallel_chunks_size=parallel_chunks_size) with zipfile.ZipFile(zip_path, "r") as zip_h: @@ -715,14 +725,14 @@ def download_assets(self, destination, progress_callback=None, parallel_download def wait_for_completion(self, status_callback=None, interval=3, max_retries=5, retry_timeout=5): """Wait for the task to complete. The call will block until the task status has become - :func:`~TaskStatus.COMPLETED`. If the status is set to :func:`~TaskStatus.CANCELED` or :func:`~TaskStatus.FAILED` + `TaskStatus.COMPLETED`. If the status is set to `TaskStatus.CANCELED` or `TaskStatus.FAILED` it raises a TaskFailedError exception. Args: - status_callback (function): optional callback that will be called with task info updates every interval seconds. - interval (int): seconds between status checks. - max_retries (int): number of repeated attempts that should be made to receive a status update before giving up. - retry_timeout (int): wait N*retry_timeout between attempts, where N is the attempt number. + status_callback (function): Optional callback that will be called with task info updates every interval seconds. + interval (int): Seconds between status checks. + max_retries (int): Number of repeated attempts that should be made to receive a status update before giving up. + retry_timeout (int): Wait N*retry_timeout between attempts, where N is the attempt number. """ retry = 0 diff --git a/pyodx/types.py b/pyodx/types.py index 2480a7b..3a4b0e3 100644 --- a/pyodx/types.py +++ b/pyodx/types.py @@ -7,7 +7,7 @@ def __str__(self): class NodeInfo(JsonResponse): """Information about a node - Args: + Attributes: version (str): Current API version task_queue_count (int): Number of tasks currently being processed or waiting to be processed total_memory (int): Amount of total RAM in the system in bytes @@ -60,17 +60,17 @@ def __init__(self, domain, help, name, value, type): class TaskInfo(JsonResponse): """Task information - Args: + Attributes: uuid (str): Unique identifier name (str): Human friendly name date_created (datetime): Creation date and time processing_time (int): Milliseconds that have elapsed since the start of processing, or -1 if no information is available. - status (:func:`pyodx.types.TaskStatus`): status (running, queued, etc.) - last_error (str): if the task fails, this will be set to a string representing the last error that occured, otherwise it's an empty string. - options (dict): options used for this task + status (TaskStatus): Status (running, queued, etc.) + last_error (str): If the task fails, this will be set to a string representing the last error that occured, otherwise it's an empty string. + options (dict): Options used for this task images_count (int): Number of images (+ GCP file) progress (float): Percentage progress (estimated) of the task - output ([str]): Optional console output (one list item per row). This is populated only if the with_output parameter is passed to info(). + output (list[str]): Optional console output (one list item per row). This is populated only if the with_output parameter is passed to info(). """ def __init__(self, json): self.uuid = json['uuid'] @@ -89,7 +89,7 @@ def __init__(self, json): class TaskStatus(Enum): """Task status - Args: + Attributes: QUEUED: Task's files have been uploaded and are waiting to be processed. RUNNING: Task is currently being processed. FAILED: Task has failed for some reason (not enough images, out of memory, etc.