also build is shared

This commit is contained in:
dcore94 2023-05-03 09:59:39 +02:00
parent 03bf5afd21
commit 8259345ff1
40 changed files with 17469 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
build/html/.buildinfo Normal file
View File

@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: d214d8ddf78ef93f993e46520f940559
tags: 645f666f9bcd5a90fca523b33c5a78b7

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -0,0 +1,2 @@
Developer manual
================

View File

@ -0,0 +1,23 @@
.. CCP Documentation documentation master file, created by
sphinx-quickstart on Thu Apr 27 09:47:54 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to CCP's documentation!
=============================================
.. toctree::
:maxdepth: 2
:caption: Contents:
introduction
usermanual/index
developermanual/index
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,53 @@
Introduction
============
About
-----
CCP is the D4Science Cloud Computing Platform built upon the experience of the previous Dataminer initiative <https://en.wikipedia.org/wiki/D4Science> and uses a novel approach based on containerization, REST APIs and Json.
Several fields of ICT have experienced a major evolution during the last decade and many new advances, such as the widespread adoption of microservice development patterns. This resulted in substantial improvements in terms of interoperability and composability of software artefacts.
The vast landscape of new opportunities, in addition to the greatly increased requirements and expectations, have been the drivers for the design and development of a new Cloud Computing Platform that represents the result of a global rethink of the Data Miner.
Architecture
------------
A logical vision of the CCP architecutre is depicted in the following Figure.
.. figure:: images/logicalvisionarchitecture.png
:alt: Logical vision of architecture
The CCP logical vision of architecture
In this vision, CCP is a layered set of components starting at the bottom with the **Infrastructure** layer, encompassing components such as hardware, Virtual Machines, container based clusters, storage facilities and networks.
The **Runtimes** layer offers a set of prebuilt, preconfigured execution environments such as containers or Virtual Machine images.
The **Method** layer contains specification of computational methods that can be anything, from social mining algorithms to AI classifiers and data harvesters. Data scientists with development skills are encouraged to develop new Methods or cloning existing ones, being their responsibility to choose compatible runtimes or propose new ones to be integrated. Tools for sharing the Methods with communities such as Virtual Research Environments are made available at this layer.
The overall user community works at the **Workbench** layer, which is the abstraction of overarching tools that are able to directly use the available Methods, compose them into workflows and integrate them into visual tools, such as Jupyter Notebook, R scripts, Knime workflows.
**Experimentation** is the term that defines the activity of configuring new Runtimes, defining new Methods and using them in the Workbench.
In the opposite direction, **Consolidation** represents the possibility to transform dynamic objects into more static ones in order to improve reusability, portability and overall performance. For instance, workflows or combinations of Methods could be transformed into Methods themselves or even Methods into Runtimes.
The logical architecture presented in Figure 9 shows the natively distributed nature of CCP.
.. figure:: images/logicalarchitecture.png
:alt: Logical architecture
The CCP logical architecture
Starting from the top, **Infrastructures** (as computing resources that will host CCP method executions, i.e. anything from simple laptops up to clusters of server Hosts) can be connected as runtime execution environments by installing a Controller component. Within an infrastructure, Hosts are computational nodes like physical or virtual servers. They are delegated to execute methods.
**Controllers** are processes that communicate through a specific API with the CCP in order to poll for tasks to perform on the Infrastructure they control. Tasks may include deploying and running methods, cleaning up executions, reporting on the overall status of the Infrastructure.
In order to keep the current state for CCP, a couple of registries are involved. Specifically, the Method & Execution Registry and the Infrastructure and Runtime registry.
User driven visual components are available to manage Infrastructures, Runtimes, Methods and Executions at the frontend. Those components are identified by the user icon in the previous Figure.
Because many of the operations involved are lengthy and asynchronous, CCP includes a Logging Service that is used to send back realtime notifications about the state of a particular process to the user. These notifications include advancement of Executions, advertisement of Infrastructures status updates, and error conditions.
All complex processes involved in CCP are implemented as workflows inside a Workflow Orchestrator which, in addition to granting a high level of flexibility and customisation, allows for a centralised endpoint to monitor progress and check for errors that may occur.
At the basis of all interactions among external actors, such as users and Controllers, a strong authentication and authorisation mechanism is enforced by an Identity and Access Management software (IAM). This enables it to address security requirements as well as to implement ownership attribution and auditing.

View File

@ -0,0 +1,417 @@
User manual
===========
This manual documents the features of CCP (Cloud Computing Platform) available to scientists that want to design, integrate and execute their methods.
Infrastructures
---------------
Infrastructures are technological platforms on top of whom Methods are executed. They are identified by a unique id, a name, a description and a type.
The type defines also the encapsulation or containerisation technology used for the runtimes.
Currently CCP supports single Docker containers, Docker Swarm based clusters and LXD clusters.
It is responisibility of a Method developers to decide what infrastructure their Methods will be executed on and this restricts the type of Runtimes that can be selected.
Runtimes
--------
Runtimes are containers that encapsulate a Method. They can be seen as minimal virtual environments made of an operating system and all dependencies required by a particular Method.
The technology and the list of available Runtimes is strictly related to type of the Infrastructure. In an Infrastructure of type Docker or Docker Swarm cluster the available Runtimes are listed in a registry of Docker containers for example.
Methods
-------
Logically, Methods are computational functions or procedures. They can be implementations of algorithms or numerical recipes. data gatherings or transformations, AI modules, generation of visuals and charts. Whatever can be executed and produces a valuable scientific result that needs to be reproduceable and repeatable can be written as a Method.
CCP tries to be as lax as possible with respect to the technical constraints for Methods. It aims at supporting every language and every reasonable combination of operating systems and dependencies by providing stacks of Runtimes that provide many ready solutions but are simultaneously open to customisations.
Anatomy of a Method
~~~~~~~~~~~~~~~~~~~
At its heart a Method is a JSON structure that aggregates a section of metadata, the definition of input parameters, the description of expected outputs, instructions for customising the deploy and execute steps of its lifecycle and link to a compatible Infrastructure.
The syntax of the JSON data structure is constrained by the grammar proposed by the OGC Processes API specification (<https://ogcapi.ogc.org/processes/>).
The following code snippet illustrates an example.
.. code:: json
{
"id":"408d9dc5-ee37-4123-9f07-4294f13bce19",
"title":"JDK-8 Example maven",
"description":"Test for executing a jdk8 sample app from GitHub repository built with maven",
"version":"0.0.1",
"jobControlOptions":[
"async-execute"
],
"keywords":[
"jdk", "java", "jdk8", "java8", "maven"
],
"metadata":[
{
"title":"Marco Lettere",
"role":"author",
"href":"https://accounts.dev.d4science.org/auth/admin/realms/d4science/users/88c76e47-5881-4716-a2bf-02d3b4073574"
},
{
"role":"category",
"title":"Test"
},
{
"title":"%2Fgcube%2Fdevsec%2FCCP",
"role":"context",
"href":"https://accounts.dev.d4science.org/auth/admin/realms/d4science/clients/%2Fgcube%2Fdevsec%2FCCP"
}
],
"outputTransmission":[
"value"
],
"inputs":{
"ccpimage":{
"id":"ccpimage",
"title":"Runtime",
"description":"The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.",
"minOccurs":1,
"maxOccurs":1,
"schema":{
"type":"string",
"format":"url",
"contentMediaType":"text/plain",
"default":"nubisware/ccp-jdk8-jammy:latest",
"readonly":"true"
}
},
"repository":{
"id":"repository",
"title":"Repository URL",
"description":"Git url to repository",
"minOccurs":1,
"maxOccurs":1,
"schema":{
"type":"string",
"format":"url",
"default":"https://github.com/dcore94/jdk-maven-example"
}
},
"mainclass":{
"id":"mainclass",
"title":"Main Class",
"description":"The main class to run",
"minOccurs":1,
"maxOccurs":1,
"schema":{
"type":"string",
"default":"example.HelloWorld"
}
}
},
"outputs":{
"filetext":{
"id":"filetext",
"title":"Text output",
"description":"Some output is written in txt format to file.txt",
"minOccurs":1,
"maxOccurs":1,
"metadata":[
{
"title":"file.txt",
"role":"file",
"href":"/output/file.txt"
}
],
"schema":{
"type":"string",
"contentMediaType":"text/plain"
}
},
"filexml":{
"id":"filexml",
"title":"XML output",
"description":"Some output is written in XML format to file.xml",
"minOccurs":1,
"maxOccurs":1,
"metadata":[
{
"title":"file.xml",
"role":"file",
"href":"/ccp_data/output/file.xml"
}
],
"schema":{
"type":"string",
"contentMediaType":"application/xml"
}
},
"filejson":{
"id":"filejson",
"title":"JSON output",
"description":"Some output is written in JSON format to file.json",
"minOccurs":1,
"maxOccurs":1,
"metadata":[
{
"title":"file.json",
"role":"file",
"href":"/ccp_data/output/file.json"
}
],
"schema":{
"type":"string",
"contentMediaType":"application/json"
}
},
"filecsv":{
"id":"filecsv",
"title":"CSV output",
"description":"Some output is written in CSV format to file.csv",
"minOccurs":1,
"maxOccurs":1,
"metadata":[
{
"title":"file.csv",
"role":"file",
"href":"/output/file.csv"
}
],
"schema":{
"type":"string",
"contentMediaType":"text/csv"
}
}
},
"additionalParameters":{
"parameters":[
{
"name":"execute-script",
"value":[
"cd execution",
"mkdir -p /ccp_data/output",
"java -cp target/jdk-maven-example-0.0.1-SNAPSHOT.jar {{ mainclass }} 1>> /ccp_data/stdout.txt 2>> /ccp_data/stderr.txt",
"cp /tmp/file.* /ccp_data/output/"
]
},
{
"name":"deploy-script",
"value":[
"git clone {{ repository }} execution 1>> /ccp_data/stdout.txt 2>> /ccp_data/stderr.txt",
"cd execution",
"mvn clean package 1> /ccp_data/stdout.txt 2>> /ccp_data/stderr.txt",
"cd -"
]
},
{
"name":"undeploy-script",
"value":[]
},
{
"name":"cancel-script",
"value":[]
}
]
},
"links":[
{
"href":"infrastructures/nubisware-docker-swarm-nfs",
"rel":"compatibleWith",
"title":"Docker swarm with NFS on Nubis cluster"
}
]
}
This is an example of a Method that executes Java 8 code rooted at a main class *example.HelloWorld* and cloned from a public GitHub reposiotry. The code is built with Maven.
The keywords section contains keywords that help in searching for the Method. The metadata fields author and context show what user has created the descriptor for the Method and in which context. Methods can also contain several category metadata items that help in classifying the Method.
jobControlOptions is hardocded to "async-execute" because CCP always executes Methods in an asynchronous way.
In the example above the Method has three inputs.
**ccpimage** is required to appear exactly once. This input is always required for every Method that will be executed on CCP. It refers to the Runtime required for the Method execution. The input is a plain text string representing the reference to a container image matching the requirements of the Infrastructure. Since the example is compatible with a Docker based Infrastructure the reference is a name in Docker form *repository/image:versiontag*. This input is readonly because the default value provided at Method definition time is constrained and not editable.
**repository** is the URL to the Git repository to be cloned. It defaults to an example project but can be edited.
**mainclass** is the main class of the Java application.
The Method declares four example output files encoded as XML, JSON, CSV or plain text. As will be shown later, a Method is not required to return only wht it declares as outputs. The output declaration is used mainly for semantically enrich an output.
The additionalParameters section encodes the three scripts governing the Method's lifecycle. The lifecycle of a Method will be described in the following section. In this example, the deploy scripts takes care of cloning a Git repository passed as input parameter "reposiotry" into a target folder and build the code using Maven. The execute script builds a folder named "output", the launches the main class of the Java application and finally copies the output files (which are created in the /tmp directory by the example Java code) to the output folder. The undeploy and cancel scripts are actually no-operations because they rely on the fact that in an environment based on containers, the clean-up operations are intrinsic.
It is important to note that all inputs declared for the Method can be used as variables in the scripts by putting their id in double curly brackets. There are other variables that can be useed in addition to outputs and they will be discussed in section "Execution context of a Method".
The links section encodes the link to the Infrastructure that is declared to be compatible with the Method.
Lifecycle of a Method execution
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following Figure depicts what happens when the execution of a Method is requested by a user either by interfacing with a GUI widget of the CCP or by invoking the REST API.
.. figure:: /images/statemachine.png
:alt: Lifecycle of Method execution
Lifecycle of Method execution
The message carrying the execution request is sent to CCP and the execution starts. The first task puts the execution in *Launch* state. During this phase the Runtime for the execution is prepared. On a container based Infrastructure this usually resolves to using the *ccpimage* input parameter in order to fetch the container image from a reposiory and instantiatiate the container.
After the transition to the the Launch state, like for every other transition, the outcome of the operation is evaluated and in case of errors the process terminates by transitioning directly to the *Destroy* state thus ensuring that the infrastructure is cleaned up.
After a successful *Launch*, the Method execution moves into *Deploy* state. As shown by the script task decorator, this task is scripted meaning that by default it's a no-operation and the commands to be performed are supplied by the creator of the Method at definition time through the *deploy-script* attribute. Example operations that could occur during this phase in a deploy script are: fetching of code on Git repositories, installation of fine grained dependencies (for example *pip install -r requirements.txt*), building of code, downloading of resource files.
From the *Deploy* phase a Method execution enters the *Execute* phase. Like for the *Deploy* phase what exactly happens during this phase is determined by the *execute-script* provided by the Method creator at Method definition time. Instructions in the execute-script usually contain invocation of main code components.
The time spent in the *Execute* phase is limited by the Infrastructure. It is up the the Infrastructure manager to define what is the maximum amount of time allowed for Method execution. If the method allows it, the execution time can be futher limited by the user requesting the execution of a Method, by setting the *ccpmaxtime* input parameter.
The *Fetch* following a successful *Execute* phase is a non scriptable transition in charge of uploading the outputs of a Method execution to the Execution storage.
The following *Undeploy* phase can be used by Method developers to perform operations after the Method execution has terminated. This phase is not thought to be a cleanup task because on containerised Infrastructures the system takes autonoumously care of destroying resources at the end of a Method execution. Instead it could be used to perform extra work like notifying external systems or sharing outputs.
Finally, the *Destroy* phase is the time where the Infrastructure controller literally destroys the Runtime of the execution and all resources created during the previous phases.
Execution context of a Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
During the execution of *deploy-script*, *execute-script* and *undeploy-script* as well as during method execution it is possible to access information that is contextual to CCP, Method or Execution request.
Some information are accessible as template variables that can be used in the scripts with the *{{ var }}* syntax. Other useful information is passed directly to the Method execution as environmental varibales that can be accessed with the proper APIS that every programming language supports.
All **input parameters** are passed in the form of template variables to the scripts. This allows the script to adapt the input values to the requirements of the Method (using them directly passing as commandline arguments, setting as environmental variables, or writing to files).
There are few input parameters that are used to govern the Method execution itself rather than providing input to the Method. In particular:
- **ccpimage** as already told is required and it is used automatically during *Launch* phase in order to instantiate a container.
- **ccpmaxtime** can be used to limit the maximum execution time of a Method. The value is expressed in seconds and it is capped by the maximum time configured for the Infrastructure.
- **ccpreplicas** currently supported on Docker swarm based Infrastructures allows for creating multiple instances of a Method execution in order to obtain a coarse grained degree of parallelism.
A set of **environmental variables** is passed to the Runtime inside of which the Method is executed in order to provide additional context.
The following two environmental valuables provide context for the execution.
- **ccptaskname** is the id of the execution.
- **ccptaskid** is the index of the replica (1-based) when multiple replicas are requested with the input parameter ccpreplicas. This can be used to customise the behavior of a replica like accessing a slice of a dataset or writing output to different files.
The following variables are related to the authentication and authorization context of the Method execution. They can be used to access D4Science services in a secure and convenient way also for very long lasting executions.
.. code-block:: bash
:caption: How to use *ccptaskid* to separate output of different replicas to different files in *execute-script*.
mkdir -p /ccp_data/output && echo $RANDOM >> /ccp_data/output/`printenv ccptaskid`.txt
- **ccpiamurl** is the URL of the Identity management service.
- **ccpclientid** is the client_id to be used for requesting token renewal.
- **ccprefreshtoken** is a refresh token by which new access tokens can be requested.
- **ccpcontext** represents the context (VO or VRE) in which the Method execution has been requested.
As an example the following Python code shows how to use the variables to request a token renewal.
.. code-block:: python
:caption: How to request a login token and an UMA token for accessing D4Science service from inside Method code
#Get auth info from env variables
refreshtoken = os.environ["ccprefreshtoken"]
context = os.environ["ccpcontext"]
clientid = os.environ["ccpclientid"]
iam = os.environ["ccpiamurl"]
#Auth related functions
logindata = { 'grant_type' : 'refresh_token', 'client_id' : clientid, 'refresh_token' : refreshtoken}
loginheaders = { "Accept" : "application/json", "Content-Type" : "application/x-www-form-urlencoded"}
umadata = { 'grant_type' : 'urn:ietf:params:oauth:grant-type:uma-ticket', 'audience' : context}
umaheaders = { "Accept" : "application/json", "Content-Type" : "application/x-www-form-urlencoded"}
def getToken():
# login with offline_token
resp1 = requests.post(iam, data=logindata, headers=loginheaders)
jwt = resp1.json()
#get UMA token for context
umaheaders["Authorization"] = "Bearer " + jwt["access_token"]
resp2 = requests.post(iam, data=umadata, headers=umaheaders)
return resp2.json()["access_token"]
# Get valid token for context
tok = getToken()
# List VRE fodler content
vrefolder = requests.get(workspace + "/vrefolder", headers={"Accept" : "application/json", "Authorization" : "Bearer " + tok}).json()
A special folder is provided in the Runtime to a Method execution for storing output files. Files are currently the only way for a Method to output results by value. The folder is named **/ccp_data** and all the files written to this folder are returned in the context of the Execution as a zip archive.
Executions
----------
An **Execution** represents the instatiation of a Method through a Request. The Request carries values for the inputs declared by the Method and a list of expected outputs. A dedicated data structure is created in the Execution repository as soon as an Execution request is accepted. The data structure acts as a folder that collects the request, all the outputs and all metadata generated in order to execute a Method.
An Execution can be exported, imported and re-executed through the CCP GUI widgets or through REST API calls.
Anatomy of an Execution
~~~~~~~~~~~~~~~~~~~~~~~
The datastructure of an Execution is meant to be as atomic and as explicative as possible.
The following is a representation of the data structure representing an Execution.
- **metadata**
- **request.json** # The JSON message that requested the Execution
- **method.json** # The JSON Method descriptor
- **infrastructure.json** # The JSON Infrastructure descriptor
- **instance.json** # The JSON descriptor of the container that played as the Runtime of the Execution
- **auth**
- **jwt.json** # Authorization information of the user requesting the Execution
- **outputs**
- **output.zip** # Zip archive of all output files and folders.
Method and Execution storage
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In ordder to be able to execute a Method or to operate on an Execution they need to be kept inside the **Workbench**. The Workbench can be seen as a sort of short lived storage area that resides closely to the CCP core components.
A Method that is on the Workbench can be cloned, edited, executed, deleted, exported or archived to a offline storage area. The offline storage area is a dedicated folder CCP/methods on a user's D4Science workspace. When exported to file or archive to the worksapce a MEthod is a JSON file named like the Method's title and version.
An Execution that is on the workbench can be browsed, downloaded, re-executed, deleted or archived to an offline storage area which resides in a dedicated folder CCP/executions on a user's workspace. When downloaded to a file or archived to the workspace, an Execution is a zip archive containing a compressed binary with the structure described in the previous section.
Methods and Executions can be reimported to the Workbench Beither by uploading exported files or passing the "sharable links" obtained from the workspace.
UI Widgets
----------
A set of graphical user interaction (GUI) widgets are provided in order to allow a user to interact from browser based applications with Methods and Executions stored in the Workbench.
Method list
~~~~~~~~~~~
The *Method list* widget is a visual representantion of the list of Methods that a user is able to access in a given context either because he/she is the owner or because they are shared in the context.
The following Figure shows an example visualization of the Method list.
.. figure:: /images/methodlistwidget.png
:alt: Method list widget
Method list widget
The Method list widget is comprised of a toolbar, a search field and the list of Methods. The Methods are organized by categories as shown in [1]. For every Method the title, version, author and description are reported in the first two lines [2]. As additional information tags and compatible Infrastructure are shown [3]. There is the possibility to download a Method or a whole class and to see how many of the shown Methods are executable [4]. Methods can be not executable if their compatible Infrastrucure is not known or available. A per MEthod tollbar [5] allows to download, edit or execute a Method for. From the global toolbar it is possible to refresh the list or upload a Method from a file [6] and also to reimport an archived Method from the workspace by copying and pasting the shareable link into the proper field and clicking on the button [7].
Method editor
~~~~~~~~~~~~~
The *Method editor* widget is a visual tool for creating, deleting, editing or cloning a MEthod descriptor.
The following Figure shows an example visualization of the Method editor.
.. figure:: /images/methodeditorwidget.png
:alt: Method editor widget
Method editor widget
From the global toolbar [1] it is possible to save the edited Method or delete it or clear all the form fields. The metadata area [2] contains the controls to define all the metadata of a Method including title, version, description, tags, categories. It is also possible to choose a compatible Infrastructure from the available ones. In the input definition area [3] the user can define all the input parameters with their type, format, encoding, cardinality and default values. In the output definition area [4] the user can define all the output files that can be expected from an Execution with their type, format, encoding and cardinality. Shortcuts are added to define with one click the standard output and error chnannels of a Method. In the scripting area [5] the deploy, execute and undeploy script can be defined.
Once the Method is saved the user will automatically be added as the Author. If the user defines the method to be public, the Method is made available to all members of the context (VO or VRE) in which the operation occurred.
Method execution form
~~~~~~~~~~~~~~~~~~~~~
Execution monitor
~~~~~~~~~~~~~~~~~
REST APIs: Interacting with Methods and Executions programmatically
-------------------------------------------------------------------

View File

@ -0,0 +1,134 @@
/*
* _sphinx_javascript_frameworks_compat.js
* ~~~~~~~~~~
*
* Compatability shim for jQuery and underscores.js.
*
* WILL BE REMOVED IN Sphinx 6.0
* xref RemovedInSphinx60Warning
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}

View File

@ -0,0 +1,701 @@
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Georgia, serif;
font-size: 17px;
background-color: #fff;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: 940px;
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 220px;
}
div.sphinxsidebar {
width: 220px;
font-size: 14px;
line-height: 1.5;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #fff;
color: #3E4349;
padding: 0 30px 0 30px;
}
div.body > .section {
text-align: left;
}
div.footer {
width: 940px;
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
p.caption {
font-family: inherit;
font-size: inherit;
}
div.relations {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0;
margin: -10px 0 0 0px;
text-align: center;
}
div.sphinxsidebarwrapper h1.logo {
margin-top: -10px;
text-align: center;
margin-bottom: 5px;
text-align: left;
}
div.sphinxsidebarwrapper h1.logo-name {
margin-top: 0px;
}
div.sphinxsidebarwrapper p.blurb {
margin-top: 0;
font-style: normal;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Georgia, serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar ul li.toctree-l1 > a {
font-size: 120%;
}
div.sphinxsidebar ul li.toctree-l2 > a {
font-size: 110%;
}
div.sphinxsidebar input {
border: 1px solid #CCC;
font-family: Georgia, serif;
font-size: 1em;
}
div.sphinxsidebar hr {
border: none;
height: 1px;
color: #AAA;
background: #AAA;
text-align: left;
margin-left: 0;
width: 50%;
}
div.sphinxsidebar .badge {
border-bottom: none;
}
div.sphinxsidebar .badge:hover {
border-bottom: none;
}
/* To address an issue with donation coming after search */
div.sphinxsidebar h3.donation {
margin-top: 10px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Georgia, serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #DDD;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #EAEAEA;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
margin: 20px 0px;
padding: 10px 30px;
background-color: #EEE;
border: 1px solid #CCC;
}
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fafafa;
}
div.admonition p.admonition-title {
font-family: Georgia, serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: #fff;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.warning {
background-color: #FCC;
border: 1px solid #FAA;
}
div.danger {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.error {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.caution {
background-color: #FCC;
border: 1px solid #FAA;
}
div.attention {
background-color: #FCC;
border: 1px solid #FAA;
}
div.important {
background-color: #EEE;
border: 1px solid #CCC;
}
div.note {
background-color: #EEE;
border: 1px solid #CCC;
}
div.tip {
background-color: #EEE;
border: 1px solid #CCC;
}
div.hint {
background-color: #EEE;
border: 1px solid #CCC;
}
div.seealso {
background-color: #EEE;
border: 1px solid #CCC;
}
div.topic {
background-color: #EEE;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt, code {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
.hll {
background-color: #FFC;
margin: 0 -12px;
padding: 0 12px;
display: block;
}
img.screenshot {
}
tt.descname, tt.descclassname, code.descname, code.descclassname {
font-size: 0.95em;
}
tt.descname, code.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #EEE;
background: #FDFDFD;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.field-list p {
margin-bottom: 0.8em;
}
/* Cloned from
* https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
*/
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
table.footnote td.label {
width: .1px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
/* Matches the 30px from the narrow-screen "li > ul" selector below */
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #EEE;
padding: 7px 30px;
margin: 15px 0px;
line-height: 1.3em;
}
div.viewcode-block:target {
background: #ffd;
}
dl pre, blockquote pre, li pre {
margin-left: 0;
padding-left: 30px;
}
tt, code {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, code.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fff;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
/* Don't put an underline on images */
a.image-reference, a.image-reference:hover {
border-bottom: none;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt, a:hover code {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
li > ul {
/* Matches the 30px from the "ul, ol" selector above */
margin-left: 30px;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: #fff;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: #FFF;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: #fff;
}
div.sphinxsidebar a {
color: #AAA;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* misc. */
.revsys-inline {
display: none!important;
}
/* Make nested-list/multi-paragraph items look better in Releases changelog
* pages. Without this, docutils' magical list fuckery causes inconsistent
* formatting between different release sub-lists.
*/
div#changelog > div.section > ul > li > p:only-child {
margin-bottom: 0;
}
/* Hide fugly table cell borders in ..bibliography:: directive output */
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
border: none;
/* Below needed in some edge cases; if not applied, bottom shadows appear */
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
/* relbar */
.related {
line-height: 30px;
width: 100%;
font-size: 0.9rem;
}
.related.top {
border-bottom: 1px solid #EEE;
margin-bottom: 20px;
}
.related.bottom {
border-top: 1px solid #EEE;
}
.related ul {
padding: 0;
margin: 0;
list-style: none;
}
.related li {
display: inline;
}
nav#rellinks {
float: right;
}
nav#rellinks li+li:before {
content: "|";
}
nav#breadcrumbs li+li:before {
content: "\00BB";
}
/* Hide certain items when printing */
@media print {
div.related {
display: none;
}
}

View File

@ -0,0 +1,900 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

View File

@ -0,0 +1 @@
/* This file intentionally left blank. */

View File

@ -0,0 +1,156 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);

View File

@ -0,0 +1,14 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.1.0',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};

BIN
build/html/_static/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

10881
build/html/_static/jquery-3.6.0.js vendored Normal file

File diff suppressed because it is too large Load Diff

2
build/html/_static/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,199 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, is available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

BIN
build/html/_static/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View File

@ -0,0 +1,83 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #8f5902; font-style: italic } /* Comment */
.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
.highlight .g { color: #000000 } /* Generic */
.highlight .k { color: #004461; font-weight: bold } /* Keyword */
.highlight .l { color: #000000 } /* Literal */
.highlight .n { color: #000000 } /* Name */
.highlight .o { color: #582800 } /* Operator */
.highlight .x { color: #000000 } /* Other */
.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #8f5902 } /* Comment.Preproc */
.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
.highlight .gd { color: #a40000 } /* Generic.Deleted */
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #ef2929 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #745334 } /* Generic.Prompt */
.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */
.highlight .ld { color: #000000 } /* Literal.Date */
.highlight .m { color: #990000 } /* Literal.Number */
.highlight .s { color: #4e9a06 } /* Literal.String */
.highlight .na { color: #c4a000 } /* Name.Attribute */
.highlight .nb { color: #004461 } /* Name.Builtin */
.highlight .nc { color: #000000 } /* Name.Class */
.highlight .no { color: #000000 } /* Name.Constant */
.highlight .nd { color: #888888 } /* Name.Decorator */
.highlight .ni { color: #ce5c00 } /* Name.Entity */
.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #000000 } /* Name.Function */
.highlight .nl { color: #f57900 } /* Name.Label */
.highlight .nn { color: #000000 } /* Name.Namespace */
.highlight .nx { color: #000000 } /* Name.Other */
.highlight .py { color: #000000 } /* Name.Property */
.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */
.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */
.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */
.highlight .mb { color: #990000 } /* Literal.Number.Bin */
.highlight .mf { color: #990000 } /* Literal.Number.Float */
.highlight .mh { color: #990000 } /* Literal.Number.Hex */
.highlight .mi { color: #990000 } /* Literal.Number.Integer */
.highlight .mo { color: #990000 } /* Literal.Number.Oct */
.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */
.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */
.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #000000 } /* Name.Function.Magic */
.highlight .vc { color: #000000 } /* Name.Variable.Class */
.highlight .vg { color: #000000 } /* Name.Variable.Global */
.highlight .vi { color: #000000 } /* Name.Variable.Instance */
.highlight .vm { color: #000000 } /* Name.Variable.Magic */
.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */

View File

@ -0,0 +1,566 @@
/*
* searchtools.js
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, searchTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const [docName, title, anchor, descr, score, _filename] = item;
let listItem = document.createElement("li");
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = docUrlRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = docUrlRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr)
listItem.appendChild(document.createElement("span")).innerHTML =
" (" + descr + ")";
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms)
);
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = _(
`Search finished, found ${resultCount} page(s) matching the search query.`
);
};
const _displayNextItem = (
results,
resultCount,
searchTerms
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() });
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent !== undefined) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query: (query) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
// array of [docname, title, anchor, descr, score, filename]
let results = [];
_removeChildren(document.getElementById("search-progress"));
const queryLower = query.toLowerCase();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
let score = Math.round(100 * queryLower.length / title.length)
results.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
for (const [file, id] of foundEntries) {
let score = Math.round(100 * queryLower.length / entry.length)
results.push([
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// lookup as object
objectTerms.forEach((term) =>
results.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort((a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
});
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
results = results.reverse();
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, searchTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
const arr = [
{ files: terms[word], score: Scorer.term },
{ files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord) && !terms[word])
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord) && !titleTerms[word])
arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
});
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, {});
scoreMap.get(file)[word] = record.score;
});
});
// create the mapping
files.forEach((file) => {
if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
fileMap.get(file).push(word);
else fileMap.set(file, [word]);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords) => {
const text = Search.htmlToText(htmlText);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
_ready(Search.init);

View File

@ -0,0 +1,144 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";
const SPHINX_HIGHLIGHT_ENABLED = true
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
parent.insertBefore(
span,
parent.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const SphinxHighlight = {
/**
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms")
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
// get individual terms from highlight string
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
localStorage.removeItem("sphinx_highlight_terms")
},
initEscapeListener: () => {
// only install a listener if it is really needed
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};
_ready(SphinxHighlight.highlightSearchWords);
_ready(SphinxHighlight.initEscapeListener);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<title>Developer manual &#8212; CCP 0.1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
<script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/sphinx_highlight.js"></script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="prev" title="User manual" href="../usermanual/index.html" />
<link rel="stylesheet" href="../_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="developer-manual">
<h1>Developer manual<a class="headerlink" href="#developer-manual" title="Permalink to this heading"></a></h1>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../index.html">CCP</a></h1>
<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../introduction.html">Introduction</a></li>
<li class="toctree-l1"><a class="reference internal" href="../usermanual/index.html">User manual</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Developer manual</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../index.html">Documentation overview</a><ul>
<li>Previous: <a href="../usermanual/index.html" title="previous chapter">User manual</a></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2023, Marco Lettere.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="../_sources/developermanual/index.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

108
build/html/genindex.html Normal file
View File

@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; CCP 0.1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<link rel="index" title="Index" href="#" />
<link rel="search" title="Search" href="search.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">CCP</a></h1>
<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
<li class="toctree-l1"><a class="reference internal" href="usermanual/index.html">User manual</a></li>
<li class="toctree-l1"><a class="reference internal" href="developermanual/index.html">Developer manual</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2023, Marco Lettere.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</html>

139
build/html/index.html Normal file
View File

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<title>Welcome to CCPs documentation! &#8212; CCP 0.1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Introduction" href="introduction.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="welcome-to-ccp-s-documentation">
<h1>Welcome to CCPs documentation!<a class="headerlink" href="#welcome-to-ccp-s-documentation" title="Permalink to this heading"></a></h1>
<div class="toctree-wrapper compound">
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a><ul>
<li class="toctree-l2"><a class="reference internal" href="introduction.html#about">About</a></li>
<li class="toctree-l2"><a class="reference internal" href="introduction.html#architecture">Architecture</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="usermanual/index.html">User manual</a><ul>
<li class="toctree-l2"><a class="reference internal" href="usermanual/index.html#infrastructures">Infrastructures</a></li>
<li class="toctree-l2"><a class="reference internal" href="usermanual/index.html#runtimes">Runtimes</a></li>
<li class="toctree-l2"><a class="reference internal" href="usermanual/index.html#methods">Methods</a></li>
<li class="toctree-l2"><a class="reference internal" href="usermanual/index.html#executions">Executions</a></li>
<li class="toctree-l2"><a class="reference internal" href="usermanual/index.html#ui-widgets">UI Widgets</a></li>
<li class="toctree-l2"><a class="reference internal" href="usermanual/index.html#rest-apis-interacting-with-methods-and-executions-programmatically">REST APIs: Interacting with Methods and Executions programmatically</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="developermanual/index.html">Developer manual</a></li>
</ul>
</div>
</section>
<section id="indices-and-tables">
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this heading"></a></h1>
<ul class="simple">
<li><p><a class="reference internal" href="genindex.html"><span class="std std-ref">Index</span></a></p></li>
<li><p><a class="reference internal" href="py-modindex.html"><span class="std std-ref">Module Index</span></a></p></li>
<li><p><a class="reference internal" href="search.html"><span class="std std-ref">Search Page</span></a></p></li>
</ul>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="#">CCP</a></h1>
<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
<li class="toctree-l1"><a class="reference internal" href="usermanual/index.html">User manual</a></li>
<li class="toctree-l1"><a class="reference internal" href="developermanual/index.html">Developer manual</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="#">Documentation overview</a><ul>
<li>Next: <a href="introduction.html" title="next chapter">Introduction</a></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2023, Marco Lettere.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/index.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

View File

@ -0,0 +1,153 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<title>Introduction &#8212; CCP 0.1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="User manual" href="usermanual/index.html" />
<link rel="prev" title="Welcome to CCPs documentation!" href="index.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="introduction">
<h1>Introduction<a class="headerlink" href="#introduction" title="Permalink to this heading"></a></h1>
<section id="about">
<h2>About<a class="headerlink" href="#about" title="Permalink to this heading"></a></h2>
<p>CCP is the D4Science Cloud Computing Platform built upon the experience of the previous Dataminer initiative &lt;<a class="reference external" href="https://en.wikipedia.org/wiki/D4Science">https://en.wikipedia.org/wiki/D4Science</a>&gt; and uses a novel approach based on containerization, REST APIs and Json.</p>
<p>Several fields of ICT have experienced a major evolution during the last decade and many new advances, such as the widespread adoption of microservice development patterns. This resulted in substantial improvements in terms of interoperability and composability of software artefacts.
The vast landscape of new opportunities, in addition to the greatly increased requirements and expectations, have been the drivers for the design and development of a new Cloud Computing Platform that represents the result of a global rethink of the Data Miner.</p>
</section>
<section id="architecture">
<h2>Architecture<a class="headerlink" href="#architecture" title="Permalink to this heading"></a></h2>
<p>A logical vision of the CCP architecutre is depicted in the following Figure.</p>
<figure class="align-default" id="id1">
<img alt="Logical vision of architecture" src="_images/logicalvisionarchitecture.png" />
<figcaption>
<p><span class="caption-text">The CCP logical vision of architecture</span><a class="headerlink" href="#id1" title="Permalink to this image"></a></p>
</figcaption>
</figure>
<p>In this vision, CCP is a layered set of components starting at the bottom with the <strong>Infrastructure</strong> layer, encompassing components such as hardware, Virtual Machines, container based clusters, storage facilities and networks.</p>
<p>The <strong>Runtimes</strong> layer offers a set of prebuilt, preconfigured execution environments such as containers or Virtual Machine images.</p>
<p>The <strong>Method</strong> layer contains specification of computational methods that can be anything, from social mining algorithms to AI classifiers and data harvesters. Data scientists with development skills are encouraged to develop new Methods or cloning existing ones, being their responsibility to choose compatible runtimes or propose new ones to be integrated. Tools for sharing the Methods with communities such as Virtual Research Environments are made available at this layer.</p>
<p>The overall user community works at the <strong>Workbench</strong> layer, which is the abstraction of overarching tools that are able to directly use the available Methods, compose them into workflows and integrate them into visual tools, such as Jupyter Notebook, R scripts, Knime workflows.</p>
<p><strong>Experimentation</strong> is the term that defines the activity of configuring new Runtimes, defining new Methods and using them in the Workbench.</p>
<p>In the opposite direction, <strong>Consolidation</strong> represents the possibility to transform dynamic objects into more static ones in order to improve reusability, portability and overall performance. For instance, workflows or combinations of Methods could be transformed into Methods themselves or even Methods into Runtimes.</p>
<p>The logical architecture presented in Figure 9 shows the natively distributed nature of CCP.</p>
<figure class="align-default" id="id2">
<img alt="Logical architecture" src="_images/logicalarchitecture.png" />
<figcaption>
<p><span class="caption-text">The CCP logical architecture</span><a class="headerlink" href="#id2" title="Permalink to this image"></a></p>
</figcaption>
</figure>
<p>Starting from the top, <strong>Infrastructures</strong> (as computing resources that will host CCP method executions, i.e. anything from simple laptops up to clusters of server Hosts) can be connected as runtime execution environments by installing a Controller component. Within an infrastructure, Hosts are computational nodes like physical or virtual servers. They are delegated to execute methods.</p>
<p><strong>Controllers</strong> are processes that communicate through a specific API with the CCP in order to poll for tasks to perform on the Infrastructure they control. Tasks may include deploying and running methods, cleaning up executions, reporting on the overall status of the Infrastructure.</p>
<p>In order to keep the current state for CCP, a couple of registries are involved. Specifically, the Method &amp; Execution Registry and the Infrastructure and Runtime registry.</p>
<p>User driven visual components are available to manage Infrastructures, Runtimes, Methods and Executions at the frontend. Those components are identified by the user icon in the previous Figure.</p>
<p>Because many of the operations involved are lengthy and asynchronous, CCP includes a Logging Service that is used to send back realtime notifications about the state of a particular process to the user. These notifications include advancement of Executions, advertisement of Infrastructures status updates, and error conditions.</p>
<p>All complex processes involved in CCP are implemented as workflows inside a Workflow Orchestrator which, in addition to granting a high level of flexibility and customisation, allows for a centralised endpoint to monitor progress and check for errors that may occur.</p>
<p>At the basis of all interactions among external actors, such as users and Controllers, a strong authentication and authorisation mechanism is enforced by an Identity and Access Management software (IAM). This enables it to address security requirements as well as to implement ownership attribution and auditing.</p>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">CCP</a></h1>
<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul class="current">
<li class="toctree-l1 current"><a class="current reference internal" href="#">Introduction</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#about">About</a></li>
<li class="toctree-l2"><a class="reference internal" href="#architecture">Architecture</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="usermanual/index.html">User manual</a></li>
<li class="toctree-l1"><a class="reference internal" href="developermanual/index.html">Developer manual</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
<li>Previous: <a href="index.html" title="previous chapter">Welcome to CCPs documentation!</a></li>
<li>Next: <a href="usermanual/index.html" title="next chapter">User manual</a></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2023, Marco Lettere.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="_sources/introduction.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>

5
build/html/objects.inv Normal file
View File

@ -0,0 +1,5 @@
# Sphinx inventory version 2
# Project: CCP
# Version:
# The remainder of this file is compressed using zlib.
xÚ…A E÷œb<@5nÝÖ<C39D> “&ƸF˜´M€1ŒÝy ¯çI¤AB«5îàÿÿf~@â]°ÓÜx®V­xëäF€b r.°lœV°MD<>Õh2¯øÕ0!©Ú Göµf”8¡¤AYVÏûÃBy<>Æq×YÚu$½nÓ!YOÛ²Â4É™z—¾HFdöPø.:rÿ`UïšPgB[ä<>h¦`Ô"sˆ~ÅkdÞþþ„O/ÒGŸþ<05>

127
build/html/search.html Normal file
View File

@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; CCP 0.1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/searchtools.js"></script>
<script src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="#" />
<script src="searchindex.js" defer></script>
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1 id="search-documentation">Search</h1>
<noscript>
<div class="admonition warning">
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
</noscript>
<p>
Searching for multiple words only shows matches that contain
all words.
</p>
<form action="" method="get">
<input type="text" name="q" aria-labelledby="search-documentation" value="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
<div id="search-results">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="index.html">CCP</a></h1>
<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
<li class="toctree-l1"><a class="reference internal" href="usermanual/index.html">User manual</a></li>
<li class="toctree-l1"><a class="reference internal" href="developermanual/index.html">Developer manual</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
</ul></li>
</ul>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2023, Marco Lettere.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,497 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<title>User manual &#8212; CCP 0.1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
<script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/sphinx_highlight.js"></script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Developer manual" href="../developermanual/index.html" />
<link rel="prev" title="Introduction" href="../introduction.html" />
<link rel="stylesheet" href="../_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="user-manual">
<h1>User manual<a class="headerlink" href="#user-manual" title="Permalink to this heading"></a></h1>
<p>This manual documents the features of CCP (Cloud Computing Platform) available to scientists that want to design, integrate and execute their methods.</p>
<section id="infrastructures">
<h2>Infrastructures<a class="headerlink" href="#infrastructures" title="Permalink to this heading"></a></h2>
<p>Infrastructures are technological platforms on top of whom Methods are executed. They are identified by a unique id, a name, a description and a type.
The type defines also the encapsulation or containerisation technology used for the runtimes.
Currently CCP supports single Docker containers, Docker Swarm based clusters and LXD clusters.
It is responisibility of a Method developers to decide what infrastructure their Methods will be executed on and this restricts the type of Runtimes that can be selected.</p>
</section>
<section id="runtimes">
<h2>Runtimes<a class="headerlink" href="#runtimes" title="Permalink to this heading"></a></h2>
<p>Runtimes are containers that encapsulate a Method. They can be seen as minimal virtual environments made of an operating system and all dependencies required by a particular Method.
The technology and the list of available Runtimes is strictly related to type of the Infrastructure. In an Infrastructure of type Docker or Docker Swarm cluster the available Runtimes are listed in a registry of Docker containers for example.</p>
</section>
<section id="methods">
<h2>Methods<a class="headerlink" href="#methods" title="Permalink to this heading"></a></h2>
<p>Logically, Methods are computational functions or procedures. They can be implementations of algorithms or numerical recipes. data gatherings or transformations, AI modules, generation of visuals and charts. Whatever can be executed and produces a valuable scientific result that needs to be reproduceable and repeatable can be written as a Method.</p>
<p>CCP tries to be as lax as possible with respect to the technical constraints for Methods. It aims at supporting every language and every reasonable combination of operating systems and dependencies by providing stacks of Runtimes that provide many ready solutions but are simultaneously open to customisations.</p>
<section id="anatomy-of-a-method">
<h3>Anatomy of a Method<a class="headerlink" href="#anatomy-of-a-method" title="Permalink to this heading"></a></h3>
<p>At its heart a Method is a JSON structure that aggregates a section of metadata, the definition of input parameters, the description of expected outputs, instructions for customising the deploy and execute steps of its lifecycle and link to a compatible Infrastructure.</p>
<p>The syntax of the JSON data structure is constrained by the grammar proposed by the OGC Processes API specification (&lt;<a class="reference external" href="https://ogcapi.ogc.org/processes/">https://ogcapi.ogc.org/processes/</a>&gt;).</p>
<p>The following code snippet illustrates an example.</p>
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;408d9dc5-ee37-4123-9f07-4294f13bce19&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;JDK-8 Example maven&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;Test for executing a jdk8 sample app from GitHub repository built with maven&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="s2">&quot;0.0.1&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;jobControlOptions&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;async-execute&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;keywords&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;jdk&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;java&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;jdk8&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;java8&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;maven&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;metadata&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Marco Lettere&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;author&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;https://accounts.dev.d4science.org/auth/admin/realms/d4science/users/88c76e47-5881-4716-a2bf-02d3b4073574&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;category&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Test&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;%2Fgcube%2Fdevsec%2FCCP&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;context&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;https://accounts.dev.d4science.org/auth/admin/realms/d4science/clients/%2Fgcube%2Fdevsec%2FCCP&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;outputTransmission&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;value&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;inputs&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;ccpimage&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;ccpimage&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Runtime&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;format&quot;</span><span class="p">:</span><span class="s2">&quot;url&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;contentMediaType&quot;</span><span class="p">:</span><span class="s2">&quot;text/plain&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;default&quot;</span><span class="p">:</span><span class="s2">&quot;nubisware/ccp-jdk8-jammy:latest&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;readonly&quot;</span><span class="p">:</span><span class="s2">&quot;true&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;repository&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;repository&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Repository URL&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;Git url to repository&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;format&quot;</span><span class="p">:</span><span class="s2">&quot;url&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;default&quot;</span><span class="p">:</span><span class="s2">&quot;https://github.com/dcore94/jdk-maven-example&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;mainclass&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;mainclass&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Main Class&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;The main class to run&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;default&quot;</span><span class="p">:</span><span class="s2">&quot;example.HelloWorld&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;outputs&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;filetext&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;filetext&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Text output&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;Some output is written in txt format to file.txt&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;metadata&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;file.txt&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;file&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;/output/file.txt&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;contentMediaType&quot;</span><span class="p">:</span><span class="s2">&quot;text/plain&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;filexml&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;filexml&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;XML output&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;Some output is written in XML format to file.xml&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;metadata&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;file.xml&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;file&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;/ccp_data/output/file.xml&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;contentMediaType&quot;</span><span class="p">:</span><span class="s2">&quot;application/xml&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;filejson&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;filejson&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;JSON output&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;Some output is written in JSON format to file.json&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;metadata&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;file.json&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;file&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;/ccp_data/output/file.json&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;contentMediaType&quot;</span><span class="p">:</span><span class="s2">&quot;application/json&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;filecsv&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="s2">&quot;filecsv&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;CSV output&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="s2">&quot;Some output is written in CSV format to file.csv&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;minOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;maxOccurs&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;metadata&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;file.csv&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="s2">&quot;file&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;/output/file.csv&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;schema&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="s2">&quot;string&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;contentMediaType&quot;</span><span class="p">:</span><span class="s2">&quot;text/csv&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;additionalParameters&quot;</span><span class="p">:{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;parameters&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="s2">&quot;execute-script&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;cd execution&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;mkdir -p /ccp_data/output&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;java -cp target/jdk-maven-example-0.0.1-SNAPSHOT.jar {{ mainclass }} 1&gt;&gt; /ccp_data/stdout.txt 2&gt;&gt; /ccp_data/stderr.txt&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;cp /tmp/file.* /ccp_data/output/&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">]</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="s2">&quot;deploy-script&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;git clone {{ repository }} execution 1&gt;&gt; /ccp_data/stdout.txt 2&gt;&gt; /ccp_data/stderr.txt&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;cd execution&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;mvn clean package 1&gt; /ccp_data/stdout.txt 2&gt;&gt; /ccp_data/stderr.txt&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="s2">&quot;cd -&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">]</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="s2">&quot;undeploy-script&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:[]</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="s2">&quot;cancel-script&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:[]</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">]</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;links&quot;</span><span class="p">:[</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;href&quot;</span><span class="p">:</span><span class="s2">&quot;infrastructures/nubisware-docker-swarm-nfs&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;rel&quot;</span><span class="p">:</span><span class="s2">&quot;compatibleWith&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="s2">&quot;Docker swarm with NFS on Nubis cluster&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">]</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>This is an example of a Method that executes Java 8 code rooted at a main class <em>example.HelloWorld</em> and cloned from a public GitHub reposiotry. The code is built with Maven.</p>
<p>The keywords section contains keywords that help in searching for the Method. The metadata fields author and context show what user has created the descriptor for the Method and in which context. Methods can also contain several category metadata items that help in classifying the Method.</p>
<p>jobControlOptions is hardocded to “async-execute” because CCP always executes Methods in an asynchronous way.</p>
<p>In the example above the Method has three inputs.</p>
<p><strong>ccpimage</strong> is required to appear exactly once. This input is always required for every Method that will be executed on CCP. It refers to the Runtime required for the Method execution. The input is a plain text string representing the reference to a container image matching the requirements of the Infrastructure. Since the example is compatible with a Docker based Infrastructure the reference is a name in Docker form <em>repository/image:versiontag</em>. This input is readonly because the default value provided at Method definition time is constrained and not editable.
<strong>repository</strong> is the URL to the Git repository to be cloned. It defaults to an example project but can be edited.
<strong>mainclass</strong> is the main class of the Java application.</p>
<p>The Method declares four example output files encoded as XML, JSON, CSV or plain text. As will be shown later, a Method is not required to return only wht it declares as outputs. The output declaration is used mainly for semantically enrich an output.</p>
<p>The additionalParameters section encodes the three scripts governing the Methods lifecycle. The lifecycle of a Method will be described in the following section. In this example, the deploy scripts takes care of cloning a Git repository passed as input parameter “reposiotry” into a target folder and build the code using Maven. The execute script builds a folder named “output”, the launches the main class of the Java application and finally copies the output files (which are created in the /tmp directory by the example Java code) to the output folder. The undeploy and cancel scripts are actually no-operations because they rely on the fact that in an environment based on containers, the clean-up operations are intrinsic.</p>
<p>It is important to note that all inputs declared for the Method can be used as variables in the scripts by putting their id in double curly brackets. There are other variables that can be useed in addition to outputs and they will be discussed in section “Execution context of a Method”.</p>
<p>The links section encodes the link to the Infrastructure that is declared to be compatible with the Method.</p>
</section>
<section id="lifecycle-of-a-method-execution">
<h3>Lifecycle of a Method execution<a class="headerlink" href="#lifecycle-of-a-method-execution" title="Permalink to this heading"></a></h3>
<p>The following Figure depicts what happens when the execution of a Method is requested by a user either by interfacing with a GUI widget of the CCP or by invoking the REST API.</p>
<figure class="align-default" id="id1">
<img alt="Lifecycle of Method execution" src="../_images/statemachine.png" />
<figcaption>
<p><span class="caption-text">Lifecycle of Method execution</span><a class="headerlink" href="#id1" title="Permalink to this image"></a></p>
</figcaption>
</figure>
<p>The message carrying the execution request is sent to CCP and the execution starts. The first task puts the execution in <em>Launch</em> state. During this phase the Runtime for the execution is prepared. On a container based Infrastructure this usually resolves to using the <em>ccpimage</em> input parameter in order to fetch the container image from a reposiory and instantiatiate the container.</p>
<p>After the transition to the the Launch state, like for every other transition, the outcome of the operation is evaluated and in case of errors the process terminates by transitioning directly to the <em>Destroy</em> state thus ensuring that the infrastructure is cleaned up.</p>
<p>After a successful <em>Launch</em>, the Method execution moves into <em>Deploy</em> state. As shown by the script task decorator, this task is scripted meaning that by default its a no-operation and the commands to be performed are supplied by the creator of the Method at definition time through the <em>deploy-script</em> attribute. Example operations that could occur during this phase in a deploy script are: fetching of code on Git repositories, installation of fine grained dependencies (for example <em>pip install -r requirements.txt</em>), building of code, downloading of resource files.</p>
<p>From the <em>Deploy</em> phase a Method execution enters the <em>Execute</em> phase. Like for the <em>Deploy</em> phase what exactly happens during this phase is determined by the <em>execute-script</em> provided by the Method creator at Method definition time. Instructions in the execute-script usually contain invocation of main code components.</p>
<p>The time spent in the <em>Execute</em> phase is limited by the Infrastructure. It is up the the Infrastructure manager to define what is the maximum amount of time allowed for Method execution. If the method allows it, the execution time can be futher limited by the user requesting the execution of a Method, by setting the <em>ccpmaxtime</em> input parameter.</p>
<p>The <em>Fetch</em> following a successful <em>Execute</em> phase is a non scriptable transition in charge of uploading the outputs of a Method execution to the Execution storage.</p>
<p>The following <em>Undeploy</em> phase can be used by Method developers to perform operations after the Method execution has terminated. This phase is not thought to be a cleanup task because on containerised Infrastructures the system takes autonoumously care of destroying resources at the end of a Method execution. Instead it could be used to perform extra work like notifying external systems or sharing outputs.</p>
<p>Finally, the <em>Destroy</em> phase is the time where the Infrastructure controller literally destroys the Runtime of the execution and all resources created during the previous phases.</p>
</section>
<section id="execution-context-of-a-method">
<h3>Execution context of a Method<a class="headerlink" href="#execution-context-of-a-method" title="Permalink to this heading"></a></h3>
<p>During the execution of <em>deploy-script</em>, <em>execute-script</em> and <em>undeploy-script</em> as well as during method execution it is possible to access information that is contextual to CCP, Method or Execution request.</p>
<p>Some information are accessible as template variables that can be used in the scripts with the <em>{{ var }}</em> syntax. Other useful information is passed directly to the Method execution as environmental varibales that can be accessed with the proper APIS that every programming language supports.</p>
<p>All <strong>input parameters</strong> are passed in the form of template variables to the scripts. This allows the script to adapt the input values to the requirements of the Method (using them directly passing as commandline arguments, setting as environmental variables, or writing to files).</p>
<p>There are few input parameters that are used to govern the Method execution itself rather than providing input to the Method. In particular:</p>
<ul class="simple">
<li><p><strong>ccpimage</strong> as already told is required and it is used automatically during <em>Launch</em> phase in order to instantiate a container.</p></li>
<li><p><strong>ccpmaxtime</strong> can be used to limit the maximum execution time of a Method. The value is expressed in seconds and it is capped by the maximum time configured for the Infrastructure.</p></li>
<li><p><strong>ccpreplicas</strong> currently supported on Docker swarm based Infrastructures allows for creating multiple instances of a Method execution in order to obtain a coarse grained degree of parallelism.</p></li>
</ul>
<p>A set of <strong>environmental variables</strong> is passed to the Runtime inside of which the Method is executed in order to provide additional context.</p>
<p>The following two environmental valuables provide context for the execution.</p>
<ul class="simple">
<li><p><strong>ccptaskname</strong> is the id of the execution.</p></li>
<li><p><strong>ccptaskid</strong> is the index of the replica (1-based) when multiple replicas are requested with the input parameter ccpreplicas. This can be used to customise the behavior of a replica like accessing a slice of a dataset or writing output to different files.</p></li>
</ul>
<p>The following variables are related to the authentication and authorization context of the Method execution. They can be used to access D4Science services in a secure and convenient way also for very long lasting executions.</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text">How to use <em>ccptaskid</em> to separate output of different replicas to different files in <em>execute-script</em>.</span><a class="headerlink" href="#id2" title="Permalink to this code"></a></div>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span> mkdir -p /ccp_data/output <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="nv">$RANDOM</span> &gt;&gt; /ccp_data/output/<span class="sb">`</span>printenv ccptaskid<span class="sb">`</span>.txt
</pre></div>
</div>
</div>
<ul class="simple">
<li><p><strong>ccpiamurl</strong> is the URL of the Identity management service.</p></li>
<li><p><strong>ccpclientid</strong> is the client_id to be used for requesting token renewal.</p></li>
<li><p><strong>ccprefreshtoken</strong> is a refresh token by which new access tokens can be requested.</p></li>
<li><p><strong>ccpcontext</strong> represents the context (VO or VRE) in which the Method execution has been requested.</p></li>
</ul>
<p>As an example the following Python code shows how to use the variables to request a token renewal.</p>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text">How to request a login token and an UMA token for accessing D4Science service from inside Method code</span><a class="headerlink" href="#id3" title="Permalink to this code"></a></div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="c1">#Get auth info from env variables</span>
<span class="n">refreshtoken</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;ccprefreshtoken&quot;</span><span class="p">]</span>
<span class="n">context</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;ccpcontext&quot;</span><span class="p">]</span>
<span class="n">clientid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;ccpclientid&quot;</span><span class="p">]</span>
<span class="n">iam</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;ccpiamurl&quot;</span><span class="p">]</span>
<span class="c1">#Auth related functions</span>
<span class="n">logindata</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">&#39;grant_type&#39;</span> <span class="p">:</span> <span class="s1">&#39;refresh_token&#39;</span><span class="p">,</span> <span class="s1">&#39;client_id&#39;</span> <span class="p">:</span> <span class="n">clientid</span><span class="p">,</span> <span class="s1">&#39;refresh_token&#39;</span> <span class="p">:</span> <span class="n">refreshtoken</span><span class="p">}</span>
<span class="n">loginheaders</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;Accept&quot;</span> <span class="p">:</span> <span class="s2">&quot;application/json&quot;</span><span class="p">,</span> <span class="s2">&quot;Content-Type&quot;</span> <span class="p">:</span> <span class="s2">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">}</span>
<span class="n">umadata</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">&#39;grant_type&#39;</span> <span class="p">:</span> <span class="s1">&#39;urn:ietf:params:oauth:grant-type:uma-ticket&#39;</span><span class="p">,</span> <span class="s1">&#39;audience&#39;</span> <span class="p">:</span> <span class="n">context</span><span class="p">}</span>
<span class="n">umaheaders</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;Accept&quot;</span> <span class="p">:</span> <span class="s2">&quot;application/json&quot;</span><span class="p">,</span> <span class="s2">&quot;Content-Type&quot;</span> <span class="p">:</span> <span class="s2">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">}</span>
<span class="k">def</span> <span class="nf">getToken</span><span class="p">():</span>
<span class="c1"># login with offline_token</span>
<span class="n">resp1</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">iam</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">logindata</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">loginheaders</span><span class="p">)</span>
<span class="n">jwt</span> <span class="o">=</span> <span class="n">resp1</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="c1">#get UMA token for context</span>
<span class="n">umaheaders</span><span class="p">[</span><span class="s2">&quot;Authorization&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;Bearer &quot;</span> <span class="o">+</span> <span class="n">jwt</span><span class="p">[</span><span class="s2">&quot;access_token&quot;</span><span class="p">]</span>
<span class="n">resp2</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">iam</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">umadata</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">umaheaders</span><span class="p">)</span>
<span class="k">return</span> <span class="n">resp2</span><span class="o">.</span><span class="n">json</span><span class="p">()[</span><span class="s2">&quot;access_token&quot;</span><span class="p">]</span>
<span class="c1"># Get valid token for context</span>
<span class="n">tok</span> <span class="o">=</span> <span class="n">getToken</span><span class="p">()</span>
<span class="c1"># List VRE fodler content</span>
<span class="n">vrefolder</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">workspace</span> <span class="o">+</span> <span class="s2">&quot;/vrefolder&quot;</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;Accept&quot;</span> <span class="p">:</span> <span class="s2">&quot;application/json&quot;</span><span class="p">,</span> <span class="s2">&quot;Authorization&quot;</span> <span class="p">:</span> <span class="s2">&quot;Bearer &quot;</span> <span class="o">+</span> <span class="n">tok</span><span class="p">})</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
</pre></div>
</div>
</div>
<p>A special folder is provided in the Runtime to a Method execution for storing output files. Files are currently the only way for a Method to output results by value. The folder is named <strong>/ccp_data</strong> and all the files written to this folder are returned in the context of the Execution as a zip archive.</p>
</section>
</section>
<section id="executions">
<h2>Executions<a class="headerlink" href="#executions" title="Permalink to this heading"></a></h2>
<p>An <strong>Execution</strong> represents the instatiation of a Method through a Request. The Request carries values for the inputs declared by the Method and a list of expected outputs. A dedicated data structure is created in the Execution repository as soon as an Execution request is accepted. The data structure acts as a folder that collects the request, all the outputs and all metadata generated in order to execute a Method.</p>
<p>An Execution can be exported, imported and re-executed through the CCP GUI widgets or through REST API calls.</p>
<section id="anatomy-of-an-execution">
<h3>Anatomy of an Execution<a class="headerlink" href="#anatomy-of-an-execution" title="Permalink to this heading"></a></h3>
<p>The datastructure of an Execution is meant to be as atomic and as explicative as possible.
The following is a representation of the data structure representing an Execution.</p>
<ul class="simple">
<li><p><strong>metadata</strong></p>
<ul>
<li><p><strong>request.json</strong> # The JSON message that requested the Execution</p></li>
<li><p><strong>method.json</strong> # The JSON Method descriptor</p></li>
<li><p><strong>infrastructure.json</strong> # The JSON Infrastructure descriptor</p></li>
<li><p><strong>instance.json</strong> # The JSON descriptor of the container that played as the Runtime of the Execution</p></li>
</ul>
</li>
<li><p><strong>auth</strong></p>
<ul>
<li><p><strong>jwt.json</strong> # Authorization information of the user requesting the Execution</p></li>
</ul>
</li>
<li><p><strong>outputs</strong></p>
<ul>
<li><p><strong>output.zip</strong> # Zip archive of all output files and folders.</p></li>
</ul>
</li>
</ul>
</section>
<section id="method-and-execution-storage">
<h3>Method and Execution storage<a class="headerlink" href="#method-and-execution-storage" title="Permalink to this heading"></a></h3>
<p>In ordder to be able to execute a Method or to operate on an Execution they need to be kept inside the <strong>Workbench</strong>. The Workbench can be seen as a sort of short lived storage area that resides closely to the CCP core components.</p>
<p>A Method that is on the Workbench can be cloned, edited, executed, deleted, exported or archived to a offline storage area. The offline storage area is a dedicated folder CCP/methods on a users D4Science workspace. When exported to file or archive to the worksapce a MEthod is a JSON file named like the Methods title and version.</p>
<p>An Execution that is on the workbench can be browsed, downloaded, re-executed, deleted or archived to an offline storage area which resides in a dedicated folder CCP/executions on a users workspace. When downloaded to a file or archived to the workspace, an Execution is a zip archive containing a compressed binary with the structure described in the previous section.</p>
<p>Methods and Executions can be reimported to the Workbench Beither by uploading exported files or passing the “sharable links” obtained from the workspace.</p>
</section>
</section>
<section id="ui-widgets">
<h2>UI Widgets<a class="headerlink" href="#ui-widgets" title="Permalink to this heading"></a></h2>
<p>A set of graphical user interaction (GUI) widgets are provided in order to allow a user to interact from browser based applications with Methods and Executions stored in the Workbench.</p>
<section id="method-list">
<h3>Method list<a class="headerlink" href="#method-list" title="Permalink to this heading"></a></h3>
<p>The <em>Method list</em> widget is a visual representantion of the list of Methods that a user is able to access in a given context either because he/she is the owner or because they are shared in the context.</p>
<p>The following Figure shows an example visualization of the Method list.</p>
<figure class="align-default" id="id4">
<img alt="Method list widget" src="../_images/methodlistwidget.png" />
<figcaption>
<p><span class="caption-text">Method list widget</span><a class="headerlink" href="#id4" title="Permalink to this image"></a></p>
</figcaption>
</figure>
<p>The Method list widget is comprised of a toolbar, a search field and the list of Methods. The Methods are organized by categories as shown in [1]. For every Method the title, version, author and description are reported in the first two lines [2]. As additional information tags and compatible Infrastructure are shown [3]. There is the possibility to download a Method or a whole class and to see how many of the shown Methods are executable [4]. Methods can be not executable if their compatible Infrastrucure is not known or available. A per MEthod tollbar [5] allows to download, edit or execute a Method for. From the global toolbar it is possible to refresh the list or upload a Method from a file [6] and also to reimport an archived Method from the workspace by copying and pasting the shareable link into the proper field and clicking on the button [7].</p>
</section>
<section id="method-editor">
<h3>Method editor<a class="headerlink" href="#method-editor" title="Permalink to this heading"></a></h3>
<p>The <em>Method editor</em> widget is a visual tool for creating, deleting, editing or cloning a MEthod descriptor.</p>
<p>The following Figure shows an example visualization of the Method editor.</p>
<figure class="align-default" id="id5">
<img alt="Method editor widget" src="../_images/methodeditorwidget.png" />
<figcaption>
<p><span class="caption-text">Method editor widget</span><a class="headerlink" href="#id5" title="Permalink to this image"></a></p>
</figcaption>
</figure>
<p>From the global toolbar [1] it is possible to save the edited Method or delete it or clear all the form fields. The metadata area [2] contains the controls to define all the metadata of a Method including title, version, description, tags, categories. It is also possible to choose a compatible Infrastructure from the available ones. In the input definition area [3] the user can define all the input parameters with their type, format, encoding, cardinality and default values. In the output definition area [4] the user can define all the output files that can be expected from an Execution with their type, format, encoding and cardinality. Shortcuts are added to define with one click the standard output and error chnannels of a Method. In the scripting area [5] the deploy, execute and undeploy script can be defined.</p>
<p>Once the Method is saved the user will automatically be added as the Author. If the user defines the method to be public, the Method is made available to all members of the context (VO or VRE) in which the operation occurred.</p>
</section>
<section id="method-execution-form">
<h3>Method execution form<a class="headerlink" href="#method-execution-form" title="Permalink to this heading"></a></h3>
</section>
<section id="execution-monitor">
<h3>Execution monitor<a class="headerlink" href="#execution-monitor" title="Permalink to this heading"></a></h3>
</section>
</section>
<section id="rest-apis-interacting-with-methods-and-executions-programmatically">
<h2>REST APIs: Interacting with Methods and Executions programmatically<a class="headerlink" href="#rest-apis-interacting-with-methods-and-executions-programmatically" title="Permalink to this heading"></a></h2>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../index.html">CCP</a></h1>
<h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../introduction.html">Introduction</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">User manual</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#infrastructures">Infrastructures</a></li>
<li class="toctree-l2"><a class="reference internal" href="#runtimes">Runtimes</a></li>
<li class="toctree-l2"><a class="reference internal" href="#methods">Methods</a></li>
<li class="toctree-l2"><a class="reference internal" href="#executions">Executions</a></li>
<li class="toctree-l2"><a class="reference internal" href="#ui-widgets">UI Widgets</a></li>
<li class="toctree-l2"><a class="reference internal" href="#rest-apis-interacting-with-methods-and-executions-programmatically">REST APIs: Interacting with Methods and Executions programmatically</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../developermanual/index.html">Developer manual</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../index.html">Documentation overview</a><ul>
<li>Previous: <a href="../introduction.html" title="previous chapter">Introduction</a></li>
<li>Next: <a href="../developermanual/index.html" title="next chapter">Developer manual</a></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&copy;2023, Marco Lettere.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 5.3.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
<a href="../_sources/usermanual/index.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>