Commit 785d9814 authored by Jiahang Chen's avatar Jiahang Chen
Browse files

Merge branch 'feature_documentation_alb' into 'feature_documentation'

Feature documentation alb

See merge request !5
parents 45cfa7ff 030313e7
Pipeline #370419 passed with stages
in 37 seconds
API Reference
-------------
This manual provides detailed information about classes and their
methods.
.. toctree::
thing
feature
identifier
role
dt_factory
tools
......@@ -50,9 +50,10 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'bizstyle'
# html_theme = 'bizstyle'
html_theme = 'classic'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
\ No newline at end of file
html_static_path = ['_static']
Feature
=========================================
.. toctree::
:maxdepth: 2
:titlesonly:
:caption: Contents:
.. automodule:: ml.feature
:members:
:special-members: __init__
Identifier
=========================================
.. toctree::
:maxdepth: 2
:titlesonly:
:caption: Contents:
.. automodule:: ml.identifier
:members:
:special-members: __init__
......@@ -3,9 +3,8 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Python Reference Implementation of fml40's documentation!
====================================================================
This a documentation for the Python Reference Implementation of fml40 library by Kompetenzzentrum Wald und Holz 4.0 (KWH4.0).
Welcome to the documentation of the fml40 python reference implementation!
==========================================================================
The documentation is composed of some preliminaries for installation, a quick start and the reference implementation package.
We have implemented a permanent Digital Twin which runs in our internal server. In order to communicate with it, you can use the `hmi.py` in the folder `demo`
......@@ -17,9 +16,8 @@ Table of Contents
md/preliminaries.md
md/quick_start.md
dt_factory
tools
md/permanent_dt.md
api_reference
Indices and tables
==================
......
......@@ -2,8 +2,9 @@ # Preliminaries
## Requirements
* Python 3+
* recommended: virtualenv and optionally, but also recommended virtualenvwrapper
To install virtualenv and virtualenvwrapper, just execute (after installing Python):
* recommended: virtualenv and optionally, but also recommended virtualenvwrapper.
In order to install virtualenv and virtualenvwrapper, just execute (after installing Python):
```
pip install virtualenv
pip install virtualenvwrapper-win
......
Role
=========================================
.. toctree::
:maxdepth: 2
:titlesonly:
:caption: Contents:
.. automodule:: ml.role
:members:
:special-members: __init__
Thing
=========================================
.. toctree::
:maxdepth: 2
:titlesonly:
:caption: Contents:
.. automodule:: ml.thing
:members:
:special-members: __init__
"""This module provides a logger."""
import logging
import os
......@@ -25,7 +27,7 @@ def setup_logger(dt_name, app_logger=APP_LOGGER):
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(log_formatter)
# stream_handler.setLevel(logging.DEBUG)
# stream_handler.setLevel(logging.DEBUG)
file_handler = logging.FileHandler(filename="./logs/{}.log".format(dt_name))
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(log_formatter)
......
from s3i import IdentityProvider, TokenType
"""This module provides functionalities for setting up a command line
parser and some functions to extract data from parsed command line arguments.
"""
import argparse
import json
from s3i import IdentityProvider, TokenType
GRANT_TYPES = ("client_credentials", "password")
def configure_client_parser(parser):
"""Adds subparsers to parser allowing the specification of a
client via file or terminal input.
:param parser: Parser to be modified
:returns: Modified parser
:rtype: argparse.ArgumentParser
"""
# Setup a parser for file input
subparsers = parser.add_subparsers(required=True, dest="client")
client_file_parser = subparsers.add_parser(
"cfi", help="Specify client and seceret via file input.")
"cfi", help="Specify client and seceret via file input."
)
client_file_parser.add_argument("cfp", help="Filepath")
# Setup a parser for user input
client_ui_parser = subparsers.add_parser(
"cui", help="Specify client and secret via terminal.")
client_ui_parser.add_argument("-c",
default="admin-cli",
help="Client name: admin-cli is default")
"cui", help="Specify client and secret via terminal."
)
client_ui_parser.add_argument(
"-c", default="admin-cli", help="Client name: admin-cli is default"
)
client_ui_parser.add_argument(
"-cs", default="", help="Client secret: empty string is default")
"-cs", default="", help="Client secret: empty string is default"
)
return client_file_parser, client_ui_parser
def configure_user_parser(parser):
"""Adds subparsers to parser allowing the specification of a
username and a password via file or terminal input.
:param parser: Parser to be modified
:returns: Modified parser
:rtype: argparse.ArgumentParser
"""
# Setup a parser for file input
subparsers = parser.add_subparsers(required=True, dest="user")
user_file_parser = subparsers.add_parser(
"ufi", help="Specifiy username and password via file input.")
"ufi", help="Specifiy username and password via file input."
)
user_file_parser.add_argument("ufp", help="Filepath to credentials.")
# Setup a parser for user input
user_ui_parser = subparsers.add_parser(
"uui", help="Specify username and password via terminal.")
"uui", help="Specify username and password via terminal."
)
user_ui_parser.add_argument("-u", required=True, help="Username")
user_ui_parser.add_argument("-up", required=True, help="User password")
return user_file_parser, user_ui_parser
def configure_credentials_parser(parser):
parser.add_argument("--g",
choices=GRANT_TYPES,
default=GRANT_TYPES[0],
help=f"Specify grant type ({GRANT_TYPES[0]} is default)")
parser.add_argument("--scope",
default="email",
help="Specifiy the scope (email is default)")
cfi_parser, cui_parser = configure_client_parser(parser)
"""Adds arguments to parser allowing to specify the grant type
used to retrieve a java web token. Afterwards
configure_client_parser and configure_user_parser are called.
:param parser: Parser to be modified
:returns: Modified parser
:rtype: argparse.ArgumentParser
"""
parser.add_argument(
"--g",
choices=GRANT_TYPES,
default=GRANT_TYPES[0],
help=f"Specify grant type ({GRANT_TYPES[0]} is default)",
)
parser.add_argument(
"--scope", default="email", help="Specifiy the scope (email is default)"
)
cfi_parser, cui_parser = configure_client_parser(parser)
__cfi_ufi_parser, __cfi_uui_parser = configure_user_parser(cfi_parser)
__cui_ufi_parser, __cui_uui_parser = configure_user_parser(cui_parser)
return parser
def get_token_from_args(args):
"""Extracts the credentials from args and returns a java web
token.
:param args: argparse.Namespace object
:returns: A java webo token or an error message.
:rtype: str
"""
grant_type = args.g
scope = args.scope
client = None
......@@ -75,12 +118,23 @@ def get_token_from_args(args):
username = args.u
password = args.up
token = get_access_token(grant_type, client, secret, username, password,
token = get_access_token(grant_type,
client,
secret,
username,
password,
scope)
return str(token)
def parse_username_and_password(filepath):
"""Reads the file specified by filepath and returns username and
password.
:param filepath: Path to a json file
:returns: Username and password
:rtype: (str, str)
"""
username = None
password = None
if filepath is not None:
......@@ -92,6 +146,13 @@ def parse_username_and_password(filepath):
def parse_client_id_and_secret(filepath):
"""Reads the file specified by filepath and returns the client id
and the client secret.
:param filepath: Path to a json file
:returns: Client id and client secret
:rtype: (str, str)
"""
client_id = None
client_secret = None
if filepath is not None:
......@@ -106,8 +167,20 @@ def parse_client_id_and_secret(filepath):
return client_id, client_secret
def get_access_token(grant_type, client_id, client_secret, username, password,
scope):
def get_access_token(grant_type, client_id, client_secret, username, password, scope):
"""Returns a java web token.
:param grant_type: method used to retrieve the token
:param client_id: s3i specific client identifier
:param client_secret: secret of the client
:param username: username
:param password: password
:param scope:
:returns: java web token
:rtype: str
"""
idp = IdentityProvider(
grant_type=grant_type,
client_id=client_id,
......
from ml.identifier import ID
"""This module implements the Feature class according to the forest
modeling language 4.0."""
import sys
from abc import ABC
import os
from abc import ABC
from ml.identifier import ID
class Feature(ABC):
"""The Feature class represents the base class for all functionalities
and values."""
def __init__(self, name="", identifier=""):
"""Constructs a new Feature class object with the given name and
identifier.
:param name: Name of the feature
:param identifier: Identifier of the feature
"""
self.__name = name
# TODO: Move setup code of __class_name into the correct setter method.
if 'ml\\fml40' in sys.modules[self.__class__.__module__].__file__:
self.__class_name = "fml40::{}".format(self.__class__.__name__)
else:
......@@ -16,38 +30,100 @@ def __init__(self, name="", identifier=""):
@property
def class_name(self):
"""Adds the prefix ml40:: to the class name and returns it.
:returns: Name of this object's class
:rtype: str
"""
return self.__class_name
@class_name.setter
def class_name(self, value):
"""Sets __class_name to value.
:param value: New class name
"""
# !!! What about the namespace?
# ??? Do we really need to have this function?
# Can't this be done implicitly?
self.__class_name = value
@property
def name(self):
"""Returns the object's name.
:returns: Name of the object
:rtype: str
"""
return self.__name
@name.setter
def name(self, value):
"""Sets __name to value.
:param value: New name
"""
self.__name = value
@property
def identifier(self):
"""Returns the object's indentifier.
:returns: Identifier
:rtype: str
"""
return self.__identifier
@identifier.setter
def identifier(self, value):
self.__identifier = ID(value).identifier
"""Builds an ID object from value and assigns the resulting identifier
to __identifier.
:param value: Proposal of the identifier
"""
self.__identifier = ID(value).identifier
@property
def subFeatures(self):
"""Returns a dict containing all subordinate features.
:returns: All subordinate features.
:rtype: dict[<str>, <Feature>]
"""
return self.__subFeatures
@subFeatures.setter
def subFeatures(self, value):
"""Replaces __subFeatures with value.
:param value: New collection of subordinate features
"""
# !!! Do we really want this? Better add and remove functions.
self.__subFeatures = value
def to_json(self):
"""Returns a json representation of this feature. Note that this
function works recursively.
:returns: Json representation
:rtype: str
"""
self.__json_out = {
"class": self.class_name,
"identifier": self.identifier,
......@@ -57,6 +133,6 @@ def to_json(self):
if self.subFeatures:
self.__json_out["subFeatures"] = list()
for key in self.subFeatures.keys():
self.__json_out["subFeatures"].append(self.subFeatures[key].to_json())
res = self.subFeatures[key].to_json()
self.__json_out["subFeatures"].append(res)
return self.__json_out
"""This module implements the class AcceptsFellingJobs."""
from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs
from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus
from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob
class AcceptsFellingJobs(AcceptsJobs):
"""This functionality signalizes that FellingJobs can be processed and
offers the possibility to remove and query it."""
def __init__(self, name="", identifier=""):
"""Initializes the object.
:param name: Object name
:param identifier: Identifier
"""
super().__init__(
name=name,
identifier=identifier)
def acceptJob(self, job: FellingJob) -> bool:
"""Accepts the given FellingJob job. Returns true if the job has been
accepted, otherwise returns false.
:param job: FellingJob to be accepted
:rtype: bool
"""
pass
def queryJobStatus(self, identifier) -> JobStatus:
"""Returns the status of the job whose id is identical to identifier.
:param identifier: Identifier of the job to be queried
:returns: Status of the job
:rtype: JobStatus
"""
pass
def removeJob(self, identifier) -> bool:
pass
"""Removes the job with the id identical to identifier. Returns true
if the job has been removed, otherwise returns false.
:param identifier: Identifier of the job to be deleted
:rtype: bool
"""
pass
"""This module implements the class AcceptsFellingSupportJobs."""
from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs
from ml.fml40.features.properties.values.documents.jobs.fellung_support_job import FellingSupportJob
class AcceptFellingSupportJobs(AcceptsJobs):
"""This functionality signalizes that FellingSupportJobs can be
processed."""
def __init__(self, name="", identifier=""):
"""Initializes the object.
:param name: Object name
:param identifier: Identifier
"""
super().__init__(
name=name,
identifier=identifier)
def acceptJob(self, job: FellingSupportJob) -> bool:
"""Accepts the given FellingSupportJob job. Returns true if the job
has been accepted, otherwise returns false.
:param job: FellingSupportJob to be accepted
:rtype: bool
"""
pass
"""This module implements the class AcceptsForwardingJobs."""
from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs
from ml.fml40.features.properties.values.documents.jobs.forwarding_job import (
ForwardingJob,
......@@ -6,12 +8,24 @@
class AcceptsForwardingJobs(AcceptsJobs):
"""This functionality signalizes that ForwadingJobs can be processed."""
def __init__(self, name="", identifier=""):
"""Initializes the object.
:param name: Object name
:param identifier: Identifier
"""
super().__init__(
name=name,
identifier=identifier)
def acceptJob(self, job: ForwardingJob) -> bool:
pass
"""Accepts the given ForwardingJob. Returns true if the job has been
accepted, otherwise returns false.
:param job: ForwardingJob to be accepted
:rtype: bool
"""
pass
"""This module implements the class AcceptsLogMeasurements."""
from ml.ml40.features.functionalities.functionality import Functionality
from ml.fml40.features.properties.values.documents.reports.log_measurement import LogMeasurement
class AcceptsLogMeasurements(Functionality):
"""This functionality signalizes that LogMeasurements can be processed."""
def __init__(self, name="", identifier=""):
"""Initializes the object.
:param name: Object name
:param identifier: Identifier
"""
super().__init__(
name=name,
identifier=identifier)
def acceptLogMeasurement(self, log_measurement: LogMeasurement) -> bool:
"""Accepts the given LogMeasurement. Returns true if the job has been
accepted, otherwise returns false.
:param job: LogMeasurement to be accepted
:rtype: bool
"""
pass
"""This module implements the class AcceptsLogTransportationJobs."""
from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs
from ml.fml40.features.properties.values.documents.jobs.log_transportation_job import LogTransportationJob
class AcceptsLogTransportationJobs(AcceptsJobs):
"""This functionality signalizes that LogTransportationJobs can be
processed."""
def __init__(self, name="", identifier=""):
"""Initializes the object.
:param name: Object name
:param identifier: Identifier
"""
super().__init__(
name=name,
identifier=identifier)
def acceptJob(self, job: LogTransportationJob) -> bool:
"""Accepts the given LogTransportationJob. Returns true if the job has
been accepted, otherwise returns false.
:param job: LogTransportationJob to be accepted
:rtype: bool
"""