Skip to content

Commit

Permalink
Automated documentation generation from docstrings and typings
Browse files Browse the repository at this point in the history
closes #18
  • Loading branch information
Oluwafemi Adenuga authored Feb 8, 2024
2 parents 10fbf67 + 98ac723 commit 7fdccf2
Show file tree
Hide file tree
Showing 7 changed files with 630 additions and 2 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
dist/
__pycache__/
__pycache__/
docs/build/
make.bat
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
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

# 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)
26 changes: 26 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "Openproject SDK"
copyright = "2024, withlogic"
author = "withlogic"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = []

templates_path = ["_templates"]
exclude_patterns = []


# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = "alabaster"
html_static_path = ["_static"]
20 changes: 20 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.. Openproject SDK documentation master file, created by
sphinx-quickstart on Fri Feb 2 16:24:28 2024.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Openproject SDK's documentation!
===========================================

.. toctree::
:maxdepth: 2
:caption: Contents:



Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
130 changes: 130 additions & 0 deletions openproject/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@


class Client:
"""
A client for interacting with the OpenProject API.
"""

def __init__(self, base_url: str, api_token: str):
"""
Initialize the client with the base URL and API token.
"""
self.base_url = base_url
self.api_version = "v3"
self.api_token = api_token
Expand All @@ -16,6 +23,9 @@ def __init__(self, base_url: str, api_token: str):
self.types = Types(self)

def _handle_response(self, response: httpx.Response):
"""
Handle the response from the API. Raises exceptions for error status codes.
"""
if response.status_code == 401:
error = response.json().get("message", "Authentication error")
raise AuthenticationError(error, response.status_code)
Expand Down Expand Up @@ -45,6 +55,9 @@ def _handle_response(self, response: httpx.Response):
def _send_request(
self, method: str, endpoint: str, params=None, data=None, **kwargs
) -> httpx.Response:
"""
Send a request to the API and return the response.
"""
url = f"{self.base_url}/api/{self.api_version}/{endpoint}"
with httpx.Client(auth=("apikey", self.api_token)) as client:
headers = {"Content-Type": "application/json"}
Expand All @@ -62,6 +75,10 @@ def __init__(self, client: Client):


class WorkPackages(SubClient):
"""
A client for interacting with the WorkPackages endpoint of the OpenProject API.
"""

_args_api_mapping = {
"_links": "_links",
"subject": "subject",
Expand All @@ -83,33 +100,74 @@ class WorkPackages(SubClient):
}

def _api_payload_from_kwargs(self, **kwargs: WorkPackage):
"""
Convert the keyword arguments to a dictionary that can be used as the payload in an API request.
:param kwargs: The keyword arguments.
:return: A dictionary that can be used as the payload in an API request.
"""
items = self._args_api_mapping.items()
data = {api_args: kwargs[args] for args, api_args in items if args in kwargs}
return data

def list(self, project: int | None):
"""
List all work packages in a project.
:param project: The ID of the project.
:return: The response from the API.
"""
params = {}
if project:
filters = [{"project": {"operator": "=", "values": project}}]
params = {"filters": json.dumps(filters)}
return self.client._send_request("GET", "work_packages", params=params)

def view(self, id: int):
"""
View a specific work package.
:param id: The ID of the work package.
:return: The response from the API.
"""
return self.client._send_request("GET", f"work_packages/{id}")

def create(self, **kwargs):
"""
Create a new work package.
:param kwargs: The properties of the work package.
:return: The response from the API.
"""
data = self._api_payload_from_kwargs(**kwargs)
return self.client._send_request("POST", "work_packages", data=data)

def update(self, id: int, **kwargs):
"""
Update a specific work package.
:param id: The ID of the work package.
:param kwargs: The new properties of the work package.
:return: The response from the API.
"""
data = self._api_payload_from_kwargs(**kwargs)
return self.client._send_request("PATCH", f"work_packages/{id}", data=data)

def delete(self, id: int):
"""
Delete a specific work package.
:param id: The ID of the work package.
:return: The response from the API.
"""
return self.client._send_request("DELETE", f"work_packages/{id}")


class Projects(SubClient):
"""
A client for interacting with the Projects endpoint of the OpenProject API.
"""

_args_api_mapping = {
"_links": "_links",
"name": "name",
Expand All @@ -118,42 +176,114 @@ class Projects(SubClient):
}

def _api_payload_from_kwargs(self, **kwargs: Project):
"""
Convert the keyword arguments to a dictionary that can be used as the payload in an API request.
:param kwargs: The keyword arguments.
:return: A dictionary that can be used as the payload in an API request.
"""
items = self._args_api_mapping.items()
data = {api_args: kwargs[args] for args, api_args in items if args in kwargs}
return data

def list(self):
"""
List all projects.
:return: The response from the API.
"""
return self.client._send_request("GET", "projects")

def view(self, id: int):
"""
View a specific project.
:param id: The ID of the project.
:return: The response from the API.
"""
return self.client._send_request("GET", f"projects/{id}")

def create(self, **kwargs):
"""
Create a new project.
:param kwargs: The properties of the project.
:return: The response from the API.
"""
data = self._api_payload_from_kwargs(**kwargs)
return self.client._send_request("POST", "projects", data=data)

def update(self, id: int, **kwargs):
"""
Update a specific project.
:param id: The ID of the project.
:param kwargs: The new properties of the project.
:return: The response from the API.
"""
data = self._api_payload_from_kwargs(**kwargs)
return self.client._send_request("PATCH", f"projects/{id}", data=data)

def delete(self, id: int):
"""
Delete a specific project.
:param id: The ID of the project.
:return: The response from the API.
"""
return self.client._send_request("DELETE", f"projects/{id}")

def list_types(self, id: int):
"""
List all types in a specific project.
:param id: The ID of the project.
:return: The response from the API.
"""
return self.client._send_request("GET", f"projects/{id}/types")


class Statuses(SubClient):
"""
A client for interacting with the Statuses endpoint of the OpenProject API.
"""

def list(self):
"""
List all statuses.
:return: The response from the API.
"""
return self.client._send_request("GET", "statuses")

def view(self, id: int):
"""
View a specific status.
:param id: The ID of the status.
:return: The response from the API.
"""
return self.client._send_request("GET", f"statuses/{id}")


class Types(SubClient):
"""
A client for interacting with the Types endpoint of the OpenProject API.
"""

def list(self):
"""
List all types.
:return: The response from the API.
"""
return self.client._send_request("GET", "types")

def view(self, id: int):
"""
View a specific type.
:param id: The ID of the type.
:return: The response from the API.
"""
return self.client._send_request("GET", f"types/{id}")
Loading

0 comments on commit 7fdccf2

Please sign in to comment.