Lib

This section contains all API references of the kadi.lib package that were not already listed in other sections.

API

class kadi.lib.api.blueprint.APIBlueprint(name: str, import_name: str, static_folder: Optional[Union[str, os.PathLike]] = None, static_url_path: Optional[str] = None, template_folder: Optional[str] = None, url_prefix: Optional[str] = None, subdomain: Optional[str] = None, url_defaults: Optional[dict] = None, root_path: Optional[str] = None, cli_group: Optional[str] = <object object>)[source]

Bases: flask.blueprints.Blueprint

Custom Flask blueprint with support for API versioning.

route(rule, **options)[source]

Decorator to register a view function for a given URL rule.

Adds a new option v to Flask’s route decorator, allowing to set an API endpoint to one or multiple specific API versions.

Example:

@route("/records", v=["1.0", "2.0"])
def get_records():
    pass

The specified API version has to be valid, i.e. it has to be listed in the API_VERSIONS value in the application’s configuration. If no versions are given, the endpoint defaults to all versions given in API_VERSIONS, i.e. if an endpoint did not change between versions it does not have to be modified or duplicated. In any case, the normal endpoint without any version will be created as well, pointing to the same function as the endpoint with the latest version.

For example, the above code would lead to the following endpoints and URLs (assuming an URL prefix of "/api"), where the last two endpoints would point to the same function:

  • api.get_records_v1_0 -> /api/v1.0/records

  • api.get_records_v2_0 -> /api/v2.0/records

  • api.get_records -> /api/records

Alternatively, the version can be set to None explicitely, in which case this decorator will behave like the standard route decorator, i.e. no versioning will be used at all. This is especially useful for internal endpoints where versioning is unnecessary.

The version is also used when generating the API documentation.

Parameters
  • rule – The URL rule as string.

  • endpoint – (optional) The endpoint for the registered URL rule. Defaults to the name of the function with the version appended, if present.

  • v – (optional) A list of strings specifying the API versions.

  • **options – Additional options to be forwarded to the underlying rule system of Flask.

kadi.lib.api.core.json_response(status_code, body=None, headers=None)[source]

Return a JSON response to a client.

Parameters
  • status_code – The HTTP status code of the response.

  • body – (optional) The response body, which must be JSON serializable.

  • headers – (optional) A dictionary of additional response headers.

Returns

The JSON response.

kadi.lib.api.core.json_error_response(status_code, message=None, description=None, headers=None, **kwargs)[source]

Return a JSON error response to a client.

Uses json_response() with the given headers and a body in the following form, assuming no additional error information was provided:

{
    "code": 404,
    "message": "<message>",
    "description": "<description>",
}
Parameters
Returns

The JSON response.

kadi.lib.api.core.create_access_token(*, name, user=None, expires_at=None, token=None, scopes=None)[source]

Convenience function to create a new personal access token including its scopes.

Uses AccessToken.create() to create the access token and also creates and links all given scopes.

Parameters
  • user – The user the access token belongs to. Defaults to the current user.

  • name – The name of the access token.

  • expires_at – (optional) The expiration date of the access token.

  • token – (optional) The actual token. Defaults to a token created by AccessToken.new_token().

  • scopes – (optional) List of scopes in the form of "<object>.<action>".

Returns

The created access token.

kadi.lib.api.core.check_access_token_scopes(*scopes, operator='and')[source]

Check if the current personal access token contains certain scopes.

Parameters
  • *scopes – One or multiple scopes in the form of "<object>.<action>". See AccessTokenScope.

  • operator – (optional) The operator the given scopes should be combined with. One of "and" or "or".

Returns

True if the access token either contains all required scopes, has full access or the current request contains no valid access token at all, False otherwise or if the given operator is invalid.

kadi.lib.api.core.scopes_required(*scopes, operator='and')[source]

Decorator to add required access token scopes to an API endpoint.

The scopes are only checked if the current request actually contains a valid personal access token. Therefore, this decorator only makes sense for public API endpoints that can be accessed using a token.

The scopes are also used when generating the API documentation.

Example:

@route("/records")
@login_required
@scopes_required("record.read")
def get_records():
    pass
Parameters
kadi.lib.api.core.internal_endpoint(func)[source]

Decorator to mark an API endpoint as internal.

Internal endpoints can only accessed via the session, not via personal access tokens.

class kadi.lib.api.schemas.AccessTokenScopeSchema(*args, _internal=None, **kwargs)[source]

Bases: kadi.lib.schemas.KadiSchema

Schema to represent access token scopes.

See AccessTokenScope.

class kadi.lib.api.schemas.AccessTokenSchema(*args, _internal=None, **kwargs)[source]

Bases: kadi.lib.schemas.KadiSchema

Schema to represent personal access tokens.

See AccessToken.

kadi.lib.api.utils.status(status_code, description)[source]

Decorator to add response status information to an API endpoint.

This information is currently only used when generating the API documentation.

Parameters
  • status_code – The status code of the response.

  • description – The description corresponding to the status code, describing when the status code occurs or whether there is a response body. Supports reST syntax.

kadi.lib.api.utils.reqbody(data, description='', type='json')[source]

Decorator to add request body information to an API endpoint.

This information is currently only used when generating the API documentation.

Parameters
  • data

    The request body information as dictionary in the following form:

    {
        "<field>": {
            "type": "integer",
            # Can be omitted, defaults to False.
            "required": True,
            # Flag indicating whether multiple values for the same key can be
            # given or not. Can be omitted, defaults to False.
            "many": False,
        }
    }
    

  • description – (optional) Additional description of the request body. Supports reST syntax.

  • type – (optional) The type of the request body, one of "json" or "form".

kadi.lib.api.utils.reqschema(schema, description='', bind=True)[source]

Decorator to add request body information to an API endpoint using a schema.

Similar to reqbody(), but the information about the request body is inferred automatically based on the given schema.

This information is also used when generating the API documentation.

Parameters
  • schema – The schema instance to use as base for the request body information.

  • description – (optional) Additional description of the request body. Supports reST syntax.

  • bind – (optional) Flag indicating whether the schema should also be injected into the decorated function as keyword argument schema. That way it can be reused more easily.

kadi.lib.api.utils.is_api_request()[source]

Check if the current request is an API request.

A request is an API request if the path of the current request path starts with "/api".

Returns

True if the request is an API request, False otherwise.

kadi.lib.api.utils.is_internal_api_request()[source]

Check if the current API request is an “internal” one.

An API request is marked as internal if it includes a query parameter _internal with any value (e.g. "https://...?_internal=true"). It does not matter whether the request uses the session or a personal access token.

Returns

True if the request is internal, False otherwise.

kadi.lib.api.utils.get_api_version()[source]

Get the API version from the current request path.

Returns

The current API version or None if the current request is not an API request or no valid version is found.

kadi.lib.api.utils.get_api_access_token()[source]

Get a personal access token from the current request.

Returns

An access token object or None if no valid bearer token can be found or no request context currently exists.

kadi.lib.api.utils.get_access_token_scopes()[source]

Get all possible access token scopes.

The possible scopes are currently combined from the ones set explicitly in API_SCOPES in the application’s configuration and from the possible permissions of the different models.

Returns

A dictionary mapping a scope’s object to its respective actions.

kadi.lib.api.utils.create_pagination_data(total, page, per_page, endpoint, **kwargs)[source]

Create pagination information for use in a JSON response.

Since the pagination data will include links to the current, next and previous “pages”, the necessary information to build said links needs to be given as well, i.e. the endpoint and its corresponding URL parameters.

Parameters
  • total – The total amount of items.

  • page – The current page.

  • per_page – Items per page.

  • endpoint – The endpoint of the current request to build links to the current, next and previous page.

  • **kwargs – Additional keyword arguments to build the links with.

Returns

The pagination information as dictionary in the following form:

{
    "_pagination": {
        "page": 2,
        "per_page": 10,
        "total_pages": 3,
        "total_items": 25,
        "_links": {
            "prev": "https://...?page=1&...",
            "self": "https://...?page=2&...",
            "next": "https://...?page=3&...",
        }
    }
}

The list of items is initially empty and can be filled afterwards with whatever data should be returned. Note that the links to the previous and next pages are only present if the respective page actually exists.

Archives

kadi.lib.archives.get_archive_contents(filename, mimetype, max_entries=100)[source]

Get information about the contents contained in an archive.

Parameters
  • filename – The filename of the archive.

  • mimetype – The MIME type of the archive. One of "application/zip", "application/gzip", "application/x-tar" or "application/x-bzip2".

  • max_entries – (optional) The maximum number of entries to collect information from. A None value will remove this limit.

Returns

An empty list if the contents could not be obtained or a list of archive entries in the following form:

[
    {
        "name": "dogs",
        "is_dir": True,
        "children": [],
    },
    {
        "name": "cat.png",
        "is_dir": False,
        "size": 12345,
    },
]

kadi.lib.archives.create_archive(filename, entries, callback=None)[source]

Create a ZIP archive containing specific files.

Files with a duplicate name will be renamed to "<basename> (<index>)<extension>". The index starts at 1 and will be incremented for each subsequent file having the same name.

Parameters
  • filename – The complete name of the new archive.

  • entries – A list of archive entries to include. Each entry must be a dictionary containing the path, the name (as it should appear in the archive) and the size of the file to include.

  • callback – (optional) A callback function that will be called after each entry that is written to the archive. The function will be called with the current number of packaged files and the current size of the archive. The callback has to return a boolean indicating whether the packaging process should continue (True) or not (False).

Returns

True if the archive was created successfully, False otherwise.

Cache

kadi.lib.cache.memoize_request(func)[source]

Decorator to cache a function call’s result during a request.

Uses an in-memory dictionary as cache that will be deleted again after the current request. The functions fully qualified name and arguments will be used as key to store its result for following calls.

Disabled during testing.

Conversion

kadi.lib.conversion.strip(value)[source]

Strip all surrounding whitespaces in one or multiple strings.

Parameters

value – A single string or a list of strings to strip.

Returns

The stripped string(s) or None if the input was None as well.

kadi.lib.conversion.normalize(value)[source]

Normalize and strip all whitespaces in one or multiple strings.

Parameters

value – A single string or a list of strings to normalize.

Returns

The normalized string(s) or None if the input was None as well.

kadi.lib.conversion.lower(value)[source]

Lowercase all characters in one or multiple strings.

Parameters

value – A single string or a list of strings to lowercase.

Returns

The lowercased string(s) or None if the input was None as well.

kadi.lib.conversion.truncate(value, length)[source]

Truncate one or multiple strings based on a given length.

Parameters
  • value – A single string or a list of strings to truncate.

  • length – The maximum length of the string.

Returns

The truncated string(s) or None if the input was None as well.

kadi.lib.conversion.recode_string(value, from_encoding='utf-8', to_encoding='utf-8')[source]

Change the encoding of a string.

Parameters
  • value – The string value.

  • from_encoding – (optional) The original encoding of the string.

  • to_encoding – (optional) The target encoding of the string.

Returns

A copy of the newly encoded string or the original value if the given value was not a string or the recoding failed.

kadi.lib.conversion.clamp(value, min_value, max_value)[source]

Return a value clamped to the inclusive range of the given min and max values.

Parameters
  • min_value – The minumum value.

  • max_value – The maximum value.

Returns

The clamped or unmodified value.

kadi.lib.conversion.none(value)[source]

Return None if a given value is falsy.

Parameters

value – A value to check for truthness.

Returns

The unmodified value or None if it is falsy.

kadi.lib.conversion.to_primitive_type(value)[source]

Convert any non-primitive value to a string.

The primitive types considered here are str, int, float, bool. A None value will also be returned as is.

Parameters

value – The value to convert.

Returns

The string representation of the value or the unmodified value if it is a primitive type or None.

kadi.lib.conversion.parse_datetime_string(value)[source]

Parse a datetime string.

Parameters

value – The datetime string to parse.

Returns

A timezone aware datetime object in UTC as specified in Python’s datetime module or None if the given string is not valid.

kadi.lib.conversion.parse_boolean_string(value)[source]

Parse a boolean string.

The given string is parsed based on typical values used for thruthness, including “true”, “t”, “yes”, “y” and “1” (case insensitive), instead of using Python’s bool conversion. All other values are considered false.

Parameters

value – The boolean string to parse.

Returns

True if the given string is considered truthy, False otherwise.

kadi.lib.conversion.parse_json_object(value)[source]

Parse a JSON object string as a dictionary.

Parameters

value – The JSON object string to parse.

Returns

The parsed dictionary or an empty dictionary if the given string is not valid.

kadi.lib.conversion.markdown_to_html(value)[source]

Render a markdown string as HTML.

Note that the resulting HTML is not safe for rendering it directly on a page.

Parameters

value – The string to render.

Returns

The rendered string or None if the input was None as well.

kadi.lib.conversion.strip_markdown(value)[source]

Strip a string of its markdown directives.

May not strip all tags, since some allowed (i.e. rendered) tags may not be standard markdown and are therefore not included in the library used to render the tags here.

Parameters

value – The string to strip.

Returns

The stripped string copy or None if the input was None as well.

Database

class kadi.lib.db.UTCDateTime(*args, **kwargs)[source]

Bases: sqlalchemy.sql.type_api.TypeDecorator

Custom timezone aware DateTime type using UTC.

As dates are currently saved without timezone information (and always interpreted as UTC), the timezone information has to be removed from datetime objects before persisting, as otherwise they are converted to local time. When retrieving the value, the timezone will be added back in.

impl

alias of sqlalchemy.sql.sqltypes.DateTime

process_bind_param(value, dialect)[source]

Convert to UTC and then remove the timezone.

process_result_value(value, dialect)[source]

Replace the missing timezone with UTC.

class kadi.lib.db.TimestampMixin[source]

Bases: object

Mixin for SQLAlchemy models to add timestamp columns.

created_at = Column(None, UTCDateTime(), table=None, nullable=False, default=ColumnDefault(<function utcnow>))

The date and time an object has been created at.

Always uses the current UTC time.

last_modified = Column(None, UTCDateTime(), table=None, nullable=False, default=ColumnDefault(<function utcnow>))

The date and time an object was last modified.

After calling register_timestamp_listener() this timestamp will automatically get updated if any column (including multivalued relationships) of the model using this mixin is updated. Always uses the current UTC time as initial value.

classmethod register_timestamp_listener()[source]

Register a listener to automatically update the last modification timestamp.

Uses SQLAlchemy’s before_flush event.

update_timestamp()[source]

Manually trigger an update to the last modification timestamp.

class kadi.lib.db.NestedTransaction(exc=<class 'sqlalchemy.exc.SQLAlchemyError'>)[source]

Bases: object

Context manager to start a “nested” transaction.

The nested transaction uses the SAVEPOINT feature of the database, which will be triggered when entering the context manager. Once the context manager exits, the savepoint is released, which does not actually persist the changes done in the savepoint yet. If the release produces an error, the savepoint will be rolled back automatically. The success attribute of the transaction can be used to check the result of the release operation.

Parameters

exc – (optional) The exception to catch when releasing the savepoint.

property success

Get the status of the nested transaction once the savepoint is released.

Will always be False before the context manager exits.

kadi.lib.db.update_object(obj, **kwargs)[source]

Convenience function to update database objects.

Only columns (i.e. attributes) that actually exist will get updated.

Parameters
  • obj – The object to update.

  • **kwargs – The columns to update and their respective values.

kadi.lib.db.composite_index(*cols)[source]

Generate a composite index.

Parameters

*cols – The names of the columns.

Returns

The Index instance.

kadi.lib.db.unique_constraint(tablename, *cols)[source]

Generate a unique constraint.

Parameters
  • tablename – The name of the table.

  • *cols – The names of the columns.

Returns

The UniqueConstraint instance.

kadi.lib.db.check_constraint(constraint, name)[source]

Generate a check constraint.

Parameters
  • constraint – The constraint expression as string.

  • name – The name of the constraint.

Returns

The CheckConstraint instance.

kadi.lib.db.length_constraint(col, min_value=None, max_value=None)[source]

Generate a length check constraint for a column.

Parameters
  • col – The name of the column.

  • min_value – (optional) Minimum length.

  • max_value – (optional) Maximum length.

Returns

The CheckConstraint instance.

kadi.lib.db.range_constraint(col, min_value=None, max_value=None)[source]

Generate a range check constraint for a column.

Parameters
  • col – The name of the column.

  • min_value – (optional) Minimum value.

  • max_value – (optional) Maximum value.

Returns

The CheckConstraint instance.

kadi.lib.db.values_constraint(col, values)[source]

Generate a values check constraint for a column.

Parameters
  • col – The name of the column.

  • values – List of values.

Returns

The CheckConstraint instance.

kadi.lib.db.generate_check_constraints(constraints)[source]

Generate database check constraints.

Supports check constraints of type "length", "range" and "values". The constraints have to be given in the following form:

{
    "col_1": {"length": {"min": 0, "max": 10}},
    "col_2": {"range": {"min": 0, "max": 10}},
    "col_3": {"values": ["val_1", "val_2"]},
}
Parameters

constraints – Dictionary of constraints to generate.

Returns

A tuple of CheckConstraint instances.

kadi.lib.db.get_class_by_tablename(tablename)[source]

Get the model class mapped to a certain database table name.

Parameters

tablename – Name of the table.

Returns

The class reference or None if the table does not exist.

kadi.lib.db.get_class_of_relationship(model, attr)[source]

Get the class of a relationship.

Parameters
  • model – The model that contains the relationship.

  • attr – Name of the relationship.

Returns

The class reference.

kadi.lib.db.is_column(model, attr)[source]

Check if a models attribute is a normal column.

Parameters
  • model – The model that contains the column.

  • attr – Name of the attribute.

Returns

True if the attribute is a column, False otherwise.

kadi.lib.db.get_column_type(model, attr)[source]

Get the type of a column.

Parameters
  • model – The model that contains the column.

  • attr – Name of the column.

Returns

The type of the column or None if the attribute is not a normal column.

kadi.lib.db.is_relationship(model, attr)[source]

Check if a models attribute is a relationship.

Parameters
  • model – The model that contains the column.

  • attr – Name of the attribute.

Returns

True if the attribute is a relationship, False otherwise.

kadi.lib.db.is_many_relationship(model, attr)[source]

Check if a models attribute is a many-relationship.

Parameters
  • model – The model that contains the column.

  • attr – Name of the attribute.

Returns

True if the attribute is a many-relationship, False otherwise.

kadi.lib.db.escape_like(value, escape='\\')[source]

Escape a string for use in LIKE queries.

Will escape "%", "_" and the escape character specified by escape.

Parameters
  • value – The string to escape.

  • escape – (optional) The escape character to use.

Returns

The escaped string.

kadi.lib.db.aquire_lock(obj)[source]

Aquire a database lock on a given object.

The database row corresponding to the given object will be locked using FOR UPDATE until the session is committed or rolled back. Once the lock is aquired, the given object is also refreshed. Should only be used if strictly necessary to prevent certain race conditions.

Parameters

obj – The object to lock.

Returns

The locked and refreshed object.

Exceptions

exception kadi.lib.exceptions.KadiException[source]

Bases: Exception

Base exception class.

exception kadi.lib.exceptions.KadiStorageError[source]

Bases: kadi.lib.exceptions.KadiException

For general file storage errors.

exception kadi.lib.exceptions.KadiFilesizeExceededError[source]

Bases: kadi.lib.exceptions.KadiStorageError

For errors relating to exceeded file size.

exception kadi.lib.exceptions.KadiFilesizeMismatchError[source]

Bases: kadi.lib.exceptions.KadiStorageError

For errors relating to file size validation.

exception kadi.lib.exceptions.KadiChecksumMismatchError[source]

Bases: kadi.lib.exceptions.KadiStorageError

For errors relating to file checksum validation.

exception kadi.lib.exceptions.KadiValidationError[source]

Bases: kadi.lib.exceptions.KadiException

For general validation errors.

exception kadi.lib.exceptions.KadiPermissionError[source]

Bases: kadi.lib.exceptions.KadiException

For general permissions errors.

exception kadi.lib.exceptions.KadiDatabaseError[source]

Bases: kadi.lib.exceptions.KadiException

For general database errors.

exception kadi.lib.exceptions.KadiDecryptionKeyError[source]

Bases: kadi.lib.exceptions.KadiDatabaseError

For errors relating to an invalid decryption key for encrypted fields.

Format

kadi.lib.format.duration(seconds)[source]

Create a human-readable, translated duration string from an amount of seconds.

Translations are only supported when having an active request context.

Parameters

seconds – The amount of seconds.

Returns

The formatted duration string.

kadi.lib.format.filesize(num_bytes)[source]

Create a human-readable file size from a given amount of bytes.

Based on Jinja’s filesizeformat filter.

Parameters

num_bytes – The amount of bytes.

Returns

The formatted file size string.

kadi.lib.format.timestamp(date_time=None, include_micro=False)[source]

Build a UTC timestamp from a specific date and time.

The timestamp will be in the form of "YYYYMMDDHHmmss".

Parameters
  • date_time – (optional) A datetime object as specified in Python’s datetime module. Defaults to the current time.

  • include_micro – (optional) Flag indicating whether to include microseconds in the timestamp as well or not.

Returns

The formatted timestamp string.

kadi.lib.format.pretty_type_name(cls_or_string)[source]

Return a pretty type name based on a class or a string.

Parameters

cls_or_string – A class reference (e.g. str) or a corresponding string (e.g. "str").

Returns

The pretty type name string.

Forms

class kadi.lib.forms.KadiForm(*args, **kwargs)[source]

Bases: flask_wtf.form.FlaskForm

Base class for all forms.

Parameters

_suffix – (optional) A suffix that will be appended to all field IDs in the form of "<id>_<suffix>". This is especially useful when dealing with multiple forms on the same page.

class kadi.lib.forms.DynamicSelectField(*args, **kwargs)[source]

Bases: wtforms.fields.core.SelectField

Custom select field for dynamically generated selections.

The instance variable initial can be used to set an initial value to prefill the selection with.

class kadi.lib.forms.DynamicMultiSelectField(*args, **kwargs)[source]

Bases: wtforms.fields.core.SelectMultipleField

Custom multi select field for dynamically generated selections.

The instance variable initial can be used to set initial values to prefill the selection with.

pre_validate(form)[source]

Does nothing since the choices are populated dynamically.

class kadi.lib.forms.TagsField(*args, **kwargs)[source]

Bases: kadi.lib.forms.DynamicMultiSelectField

Custom multi select field with support for “tagging”.

Tagging allows putting in custom values in a multi select field.

Parameters

max_len – (optional) The maximum length of each tag.

post_validate(form, validation_stopped)[source]

Validate each tag.

Does not allow empty tags and optionally checks their maximum length as well.

process_formdata(valuelist)[source]

Convert each tag to lowercase and remove/normalize whitespace.

class kadi.lib.forms.LFTextAreaField(*args, **kwargs)[source]

Bases: wtforms.fields.simple.TextAreaField

Custom text area field that converts CR LF to LF.

process_formdata(valuelist)[source]

Convert each occurence of \r\n to \n.

class kadi.lib.forms.UTCDateTimeField(*args, **kwargs)[source]

Bases: wtforms.fields.core.DateTimeField

Custom timezone aware DateTimeField using UTC.

Parameters

date_format – (optional) The date format to use for parsing and serializing.

process_formdata(valuelist)[source]

Replace the missing timezone with UTC.

class kadi.lib.forms.ValidateUUID(version=4)[source]

Bases: object

Validate a UUID of a specific version in a form field.

Parameters

version – (optional) The UUID version.

kadi.lib.forms.validate_identifier(form, field)[source]

Validate an identifier in a form field.

Uses kadi.lib.validation.validate_identifier().

Parameters
  • form – The form object.

  • field – The field object.

kadi.lib.forms.validate_mimetype(form, field)[source]

Validate a MIME type in a form field.

Uses kadi.lib.validation.validate_mimetype().

Parameters
  • form – The form object.

  • field – The field object.

kadi.lib.forms.validate_username(form, field)[source]

Validate a local username in a form field.

Uses kadi.lib.validation.validate_username().

Parameters
  • form – The form object.

  • field – The field object.

kadi.lib.forms.check_duplicate_identifier(field, model, exclude=None)[source]

Check for a duplicate identifier in a form field.

Has to be called manually after the usual validation as further arguments are needed for this check.

Parameters
  • field – The identifier field.

  • model – The model the check the identifier in.

  • exclude – (optional) An object that should be excluded in the check.

kadi.lib.forms.field_to_dict(field)[source]

Convert a form field into a dictionary representation.

Parameters

field – The form field to convert.

Returns

The converted form field.

kadi.lib.forms.json_field(field)[source]

Convert a form field into a JSON representation.

Parameters

field – The form field to convert.

Returns

The converted form field.

Jinja

class kadi.lib.jinja.SnippetExtension(environment: jinja2.environment.Environment)[source]

Bases: jinja2.ext.Extension

Jinja extension to pass variables to HTML snippets.

See render_snippet() for an explanation of the parameters.

Example:

{% snippet "my_snippet", foo=1, bar=2 %}
parse(parser)[source]

Parse the snippet tag and arguments.

kadi.lib.jinja.render_snippet(snippet, _module=None, **kwargs)[source]

Render an HTML snippet.

The snippet has to be in a directory called “snippets”. Otherwise, the same rules apply as in Flask’s render_template function.

Parameters
  • snippet – The name of the snippet without the “.html” extension.

  • _module – (optional) If the snippet is part of a module, the name of this module can be specified using this parameter. It will be prepended before the snippet path.

  • **kwargs – The keyword arguments to pass to the snippet.

Returns

The rendered snippet.

JWT

kadi.lib.jwt.encode_jwt(payload, expires_in=None)[source]

Encode a given payload inside a JSON web token.

Parameters
  • payload – The payload to encode as dictionary.

  • expires_in – (optional) The time in seconds that the token should expire in.

Returns

The encoded JWT as string.

kadi.lib.jwt.decode_jwt(token)[source]

Decode a given JSON web token.

Parameters

token – The token to decode as string.

Returns

The decoded payload dictionary or None if the token was invalid.

Licenses

class kadi.lib.licenses.schemas.LicenseSchema(*args, _internal=None, **kwargs)[source]

Bases: kadi.lib.schemas.KadiSchema

Schema to represent licenses.

See License.

kadi.lib.licenses.utils.update_licenses(licenses_url=None, catch_errors=True)[source]

Update the licenses stored in the database.

The expected license format is a dictionary, mapping the ID of each license to some additional information about the license, namely its title and (possibly empty) URL.

Parameters
  • licenses_url – (optional) The URL to get the licenses from. Defaults to LICENSES_URL as specified in the application’s configuration.

  • catch_errors – (optional) Flag indicating whether to catch any errors while retrieving or updating the licenses.

LDAP

kadi.lib.ldap.make_upn(username, dn)[source]

Create a user principal name (UPN) for use in an Active Directory.

Parameters
  • username – The name of a user to use as a prefix for the UPN.

  • dn – A DN to parse in order to retrieve the suffix of the UPN.

Returns

The UPN in the form of "<username>@<domain>" or None if the given DN could not be parsed or does not contain any domain components.

kadi.lib.ldap.bind(connection)[source]

Try to authenticate with a server given an LDAP connection.

By default, LDAP connections are anonymous. The BIND operation establishes an authentication state between a client and a server.

Parameters

connection – The connection object, see also make_connection().

Returns

True if the BIND operation was successful, False otherwise.

kadi.lib.ldap.unbind(connection)[source]

Disconnect a given LDAP connection.

Parameters

connection – The connection object, see also make_connection().

kadi.lib.ldap.make_server(host, port=389, use_ssl=False, validate_cert='REQUIRED')[source]

Create a new LDAP Server object.

Parameters
  • host – The host name or IP address of the LDAP server.

  • port – (optional) The port the LDAP server is listening on.

  • use_ssl – (optional) Flag indicating whether the whole connection should be secured via SSL.

  • validate_cert – (optional) Whether the certificate of the server should be validated. One of "NONE", "OPTIONAL" or "REQUIRED".

Returns

The new Server object or None if it could not be created.

kadi.lib.ldap.make_connection(server, user=None, password=None, use_starttls=False)[source]

Create a new LDAP Connection object.

Parameters
  • server – The server object to use for the connection. See make_server().

  • user – (optional) The user for simple BIND.

  • password – (optional) The password of the user for simple BIND.

  • use_starttls – (optional) Flag indicating whether the connection should be secured via STARTTLS.

Returns

The new Connection object or None if it could not be created.

kadi.lib.ldap.search(connection, search_base, search_filter, attribute_map, keep_list_attrs=False)[source]

Perform a search in an LDAP database given a connection.

Parameters
  • connection – The LDAP connection to use. See make_connection().

  • search_base – The base of the search request.

  • search_filter – The filter of the search request.

  • attribute_map – A dictionary mapping arbitrary names to LDAP attribute names. The former names specify the keys to use for each search result (e.g. "firstname"), while the latter names specify the name of the attribute that should be extracted from the resulting LDAP entry (e.g. "givenName").

  • keep_list_attrs – (optional) Flag to indicate if results that have multiple values should be returned as lists or not. If not, only the first value of a result will be returned.

Returns

A dictionary similar to the given attribute_map or None if no results could be retrieved. The LDAP attribute names will be replaced by the respective result value(s) in the result or with None if the attribute could not be found.

kadi.lib.ldap.modify_password(connection, user, new_password, old_password=None, active_directory=False)[source]

Modify a user’s LDAP password using an extended password modification operation.

Parameters
  • connection – The LDAP connection to use. See make_connection().

  • user – The user whose password should be changed.

  • new_password – The new password of the user.

  • old_password – (optional) The old password of the user, if the LDAP server requires it.

  • active_directory – (optional) Flag indicating whether the LDAP server is an Active Directory, which does not support the standard extended password modification operation.

Returns

A boolean value indicating whether the change was successful.

Mails

kadi.lib.mails.send_mail(*, subject, message, to_addresses, from_address=None, cc=None, bcc=None, attachments=None, reply_to=None, html_message=None, headers=None)[source]

Send an email to one or multiple recipients.

Uses the configuration values SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_TIMEOUT and SMTP_USE_TLS set in the application’s configuration for the connection.

Parameters
  • subject – The subject of the email.

  • message – The plain text body of the email.

  • to_addresses – A list of recipient addresses.

  • from_address – (optional) The sender’s email address. Defaults to the address set in MAIL_NO_REPLY in the current application’s configuration.

  • cc – (optional) A list of recipient addresses used in the “CC” header when sending the email.

  • bcc – (optional) A list of recipient addresses used in the “BCC” header when sending the email.

  • attachments – (optional) A list of attachments to put on the message. The list has to consist of triples in the form of (filename, content, mimetype). The content can either be a string or bytes object, while the MIME type will be guessed based on the given filename if omitted (i.e. set to None).

  • reply_to – (optional) A list of recipient addresses used in the “Reply-To” header when sending the email.

  • html_message – (optional) An HTML body of the email as alternative to the plain text version.

  • headers – (optional) A dictionary of additional headers to put on the message, mapping header names to their respective values.

Returns

The number of emails that were sent successfully.

OAuth

kadi.lib.oauth.core.create_oauth2_token(*, name, access_token, refresh_token=None, user=None, expires_at=None, expires_in=None)[source]

Create a new OAuth2 token.

Parameters
  • name – See OAuth2Token.name.

  • access_token – See OAuth2Token.access_token.

  • refresh_token – (optional) See OAuth2Token.refresh_token.

  • user – (optional) The user the token should belong to. Defaults to the current user.

  • expires_at – (optional) The expiration date and time of the access token as Unix timestamp. Will be preferred if expires_in is also given.

  • expires_in – (optional) The lifetime of the access token in seconds.

Returns

The created OAuth2 token.

kadi.lib.oauth.core.update_oauth2_token(oauth2_token, expires_at=None, expires_in=None, **kwargs)[source]

Update an existing OAuth2 token.

Parameters
  • oauth2_token – The OAuth2 token to update.

  • expires_at – (optional) The expiration date and time of the access token as Unix timestamp. Will be preferred if expires_in is also given.

  • expires_in – (optional) The lifetime of the access token in seconds.

  • **kwargs – Keyword arguments that will be passed to kadi.lib.db.update_object().

kadi.lib.oauth.utils.get_oauth2_token(name, user=None, delete_on_error=False, auto_refresh=False)[source]

Get an OAuth2 token of a user by its name.

Parameters
  • name – The name of the token.

  • user – (optional) The user the token belongs to. Defaults to the current user.

  • delete_on_error – (optional) Flag indicating whether the token should be deleted from the database if either the access token or refresh token cannot be decrypted due to an invalid decryption key or if the access token is expired and cannot be refreshed if auto_refresh is True.

  • auto_refresh – (optional) Flag indicating whether the underlying access token should be refreshed automatically if it is expired. This requires that the OAuth2 provider used to create the token is registered with the application and that a valid refresh token is stored as well.

Returns

The OAuth2 token or None if no token could be retrieved.

kadi.lib.oauth.utils.get_oauth2_providers(user=None)[source]

Get a list of registered OAuth2 providers.

Uses the "kadi_get_oauth2_providers" plugin hook to collect potential OAuth2 providers.

Note that this function may issue one or more database commits.

Parameters

user – (optional) The user that should be checked for whether they are connected with an OAuth2 provider, in which case "is_connected" will be set to True for the respective provider. Defaults to the current user.

Returns

A list of provider dictionaries in the following form, sorted by whether the provider is connected first and the name of the provider second:

[
    {
        "name": "example",
        "title": "Example provider",
        "website": "https://example.com",
        "description": "An example OAuth2 provider.",
        "is_connected": True,
    },
]

PDF

class kadi.lib.pdf.PDF(title='')[source]

Bases: fpdf.fpdf.FPDF

Base PDF generation class using FPDF.

Parameters

title – (optional) The title of the PDF, which will appear in the header on each page and in the metadata of the PDF itself.

static format_date(date_time, include_micro=False)[source]

Format a datetime object in a user-readable manner.

Parameters
  • date_time – The datetime object to format.

  • include_micro – (optional) Flag indicating whether to include microseconds in the formatted datetime.

Returns

The formatted datetime string.

header()[source]

Automatically prints a header on each page of the generated PDF.

footer()[source]

Automatically prints a footer on each page of the generated PDF.

truncated_cell(w, txt='', **kwargs)[source]

Print a cell with potentially truncated text based on the cell’s width.

Parameters
  • w – The width of the cell.

  • txt – (optional) The text content of the cell.

  • **kwargs – Additional keyword arguments to pass to fpdf2’s cell function.

calculate_max_height(contents)[source]

Calculate the maximum height that will be required by multiple multi-cells.

Note that this method always uses the current font family and size for its calculations.

Parameters

contents – A list of tuples containing the width, the text content and the font style of each cell.

Returns

The maximum height the cells will require.

Publications

kadi.lib.publications.get_publication_providers(user=None)[source]

Get a list of registered publication providers.

Uses the "kadi_get_publication_providers" plugin hook to collect potential publication providers combined with the information from kadi.lib.oauth.utils.get_oauth2_providers().

Note that this function may issue one or more database commits.

Parameters

user – (optional) The user that should be checked for whether they are connected with the OAuth2 provider the publication provider uses, in which case "is_connected" will be set to True for the respective provider. Defaults to the current user.

Returns

A list of provider dictionaries in the following form, sorted by name:

[
    {
        "name": "example",
        "description": "An example publication provider.",
        "title": "Example provider",
        "website": "https://example.com",
        "is_connected": True,
    },
]

kadi.lib.publications.publish_record(record, provider, user=None, task=None)[source]

Publish a record using a given provider.

Uses the "kadi_publish_record" plugin hook to select the suitable publication provider if possible.

Note that this function issues one or more database commits.

Parameters
  • record – The record to publish.

  • provider – The provider to use for publishing.

  • user – (optional) The user that is publishing the record. Defaults to the current user.

  • task – (optional) A Task object that can be provided if this function is executed in a task.

Returns

A tuple consisting of a flag indicating whether the operation succeeded and an HTML template further describing the result, depending on the provider.

Resources

Convenience function to link two resources together.

For ease of use in API endpoints. Uses kadi.lib.resources.utils.add_link().

Note that this function may issue a database commit.

Parameters
Returns

A JSON response depending on the success of the operation.

Convenience function to remove the link between two resources.

For ease of use in API endpoints. Uses kadi.lib.resources.utils.remove_link().

Note that this function may issue a database commit.

Parameters
Returns

A JSON response depending on the success of the operation.

kadi.lib.resources.api.add_role(subject, resource, role_name, user=None)[source]

Convenience function to add an existing role to a user or group.

For ease of use in API endpoints. Uses kadi.modules.permissions.utils.add_role().

Note that this function may issue a database commit.

Parameters
  • subject – See kadi.modules.permissions.utils.add_role().

  • resource – The resource the role refers to, an instance of Record, Collection, Group or Template.

  • role_name – See kadi.modules.permissions.utils.add_role().

  • user – (optional) The user performing the operation. Defaults to the current user.

Returns

A JSON response depending on the success of the operation.

kadi.lib.resources.api.remove_role(subject, resource)[source]

Convenience function to remove an existing role of a user or group.

For ease of use in API endpoints. Uses kadi.modules.permissions.utils.remove_role(). If the given subject is the creator of the given resource, the role will not be removed.

Note that this function may issue a database commit.

Parameters
  • subject – See kadi.modules.permissions.utils.remove_role().

  • resource – The resource the role refers to, an instance of Record, Collection, Group or Template.

Returns

A JSON response depending on the success of the operation.

kadi.lib.resources.api.change_role(subject, resource, role_name)[source]

Convenience function to change an existing role of a user or group.

For ease of use in API endpoints. If the given subject is the creator of the given resource, the role will not be changed. Uses kadi.modules.permissions.utils.remove_role() and kadi.modules.permissions.utils.add_role().

Note that this function may issue a database commit or rollback.

Parameters
  • subject – The user or group.

  • resource – The resource the role refers to, an instance of Record, Collection, Group or Template.

  • role_name – The name of the role to change.

Returns

A JSON response depending on the success of the operation.

kadi.lib.resources.api.get_selected_resources(model, page=1, term='', exclude=None, actions=None, filters=None, user=None)[source]

Convenience function to search resources for use in dynamic selections.

For ease of use in API endpoints. Used in conjunction with “Select2” to search resources via dyamic selection fields. Only the resources the given user has read permission for are returned.

Parameters
  • model – The resource model to search, one of Record, Collection or Group.

  • page – (optional) The current page.

  • term – (optional) The search term. Will be used to search the title and identifier of the resource (case insensitive).

  • exclude – (optional) A list of resource IDs to exclude in the results. Defaults to an empty list.

  • actions – (optional) Further actions to check the access permissions for.

  • filters – (optional) Additional filter expressions to apply to the respective resource query.

  • user – (optional) The user performing the search. Defaults to the current user.

Returns

A JSON response containing the results in the following form, assuming the search results consist of a single Record:

{
    "results": [
        {
            "id": 1,
            "text": "@sample-record-1",
            "body": "<optional HTML body>",
        }
    ],
    "pagination": {"more": false},
}

kadi.lib.resources.api.get_resource_user_roles(resource, page=1, per_page=10, filter_term='', exclude=None)[source]

Get the paginated user roles of a resource.

Parameters
  • resource – The resource to get the user roles of. One of Record, Collection or Template.

  • page – (optional) The current page. :param per_page: (optional) Items per page.

  • filter_term – (optional) A query to filter the users by their username or display name.

  • exclude – (optional) A list of user IDs to exclude in the results.

Returns

A tuple containing a list of deserialized user roles and the total amount of user roles.

kadi.lib.resources.api.get_resource_group_roles(resource, page=1, per_page=10, filter_term='', user=None)[source]

Get the paginated group roles of a resource.

This includes the special case of the given user not having read access to a group (any more) but wanting to change/remove permissions of the group, if the permissions of the user allow them to. Since such groups should still be listed, we include them using a limited subset of group attributes.

Parameters
  • resource – The resource to get the group roles of. One of Record, Collection or Template.

  • page – (optional) The current page. :param per_page: (optional) Items per page.

  • filter_term – (optional) A query to filter the groups by their title or identifier.

  • user – (optional) The user to check for any permissions regarding the resulting groups. Defaults to the current user.

Returns

A tuple containing a list of deserialized group roles and the total amount of user roles.

Convenience function to link two resources together.

Note that only the link-permission of the given resource is checked.

Parameters
  • relationship – The many-relationship to append the resource to.

  • resource – The resource to link, an instance of Record or Collection.

  • user – (optional) The user performing the link operation. Defaults to the current user.

Returns

True if the link was established successfully, False if the link already exists.

Raises

KadiPermissionError – If the user performing the operation does not have the necessary permissions.

Convenience function to remove the link between two resources.

Note that only the link-permission of the given resource is checked.

Parameters
  • relationship – The many-relationship to remove the resource from.

  • resource – The resource to remove, an instance of Record or Collection.

  • user – (optional) The user performing the link operation. Defaults to the current user.

Returns

True if the link was removed successfully, False if the link does not exist.

Raises

KadiPermissionError – If the user performing the operation does not have the necessary permissions.

kadi.lib.resources.utils.get_linked_resources(model, relationship, actions=None, user=None)[source]

Convenience function to get all linked resources that a user can access.

In this context having access to a resource means having read permission for that resource.

Parameters
  • model – The resource model of which to get the links from, one of Record or Collection.

  • relationship – The many-relationship that represents the linked resources to get.

  • actions – (optional) Further actions to check the access permissions for.

  • user – (optional) The user that will be checked for access permissions. Defaults to the current user.

Returns

The resulting query of the linked resources.

kadi.lib.resources.utils.search_resources(model, query=None, sort='_score', filter_ids=None, page=1, per_page=10)[source]

Convenience function to query the search index for a specific model.

Uses SearchableMixin.search() for the given model.

Parameters
  • model – The resource model to query, one of Record, Collection or Group.

  • query – (optional) See SearchableMixin.search().

  • sort – (optional) The name of a field to sort on. One of "_score", "last_modified", "-last_modified", "created_at", "-created_at", "title", "-title", "identifier" or "-identifier". Defaults to "_score" if a query is given and to "-last_modified" otherwise.

  • filter_ids – (optional) See SearchableMixin.search().

  • page – (optional) The current page.

  • per_page – (optional) Search results per page.

Returns

A tuple containing a list of the search results and the total amount of hits.

Convenience function to link multiple resources together.

For ease of use in view functions. Uses kadi.lib.resources.utils.add_link() but silently ignores any errors.

Parameters
kadi.lib.resources.views.add_roles(model, subject_ids, resource, role_name, user=None)[source]

Convenience function to add an existing role to users or groups.

For ease of use in view functions. Uses kadi.modules.permissions.utils.add_role() but silently ignores any errors.

Parameters
  • model – The model of the subject, one of User or Group.

  • subject_ids – A list of subject IDs that the role should be added to.

  • resource – The resource the role refers to, an instance of Record, Collection, Group or Template.

  • user – (optional) The user performing the operation. Defaults to the current user.

  • role_name – See kadi.modules.permissions.utils.add_role().

kadi.lib.resources.views.remove_roles(model, subject_ids, resource)[source]

Convenience function to remove roles of users or groups.

For ease of use in view functions. Uses kadi.modules.permissions.utils.remove_role(). In case of users, the resource creator’s role will always stay intact.

Parameters
  • model – The model of the subject, one of User or Group.

  • subject_ids – A list of subject IDs that the role should be removed from.

  • resource – The resource the role refers to, an instance of Record, Collection, Group or Template.

kadi.lib.resources.views.copy_roles(resource, resource_id)[source]

Convenience function to copy the roles of another resource.

For ease of use in view functions. The creator of the new resource needs permission to read the resource to copy the roles from. Additionally, only group roles of groups that the creator can read are copied.

Parameters
  • resource – The resource the new roles should be added to. An instance of Record, Collection or Group.

  • resource_id – The ID of the resource to copy the roles from. The type of this object will always be the same than the one of resource.

Revisions

kadi.lib.revisions.core.create_revision(obj, user=None)[source]

Create a new revision of an object.

If none of the revisioned values changed, no new revision will be created. See also kadi.lib.revisions.core.setup_revisions().

Note that this function aquires a lock on the given object.

Parameters
  • obj – The object to create a new revision for.

  • user – (optional) The user that triggered the revision. Defaults to the current user.

Returns

True if a new revision was created, False otherwise.

kadi.lib.revisions.core.delete_revisions(obj)[source]

Delete all revisions of an object.

Parameters

obj – The object to delete the revisions of.

kadi.lib.revisions.core.setup_revisions()[source]

Setup revisioning for all models that support it.

The columns to store revisions of have to be specified in a Meta.revision attribute in each model. It should be a list of strings specifying the attribute names.

Example:

class Foo:
    class Meta:
        revision = ["bar", "baz[foo, bar]"]

The columns can either be simple columns, like the first value in the list, or relationships like the second value. For the latter, all columns of the relationship that should be included in the revision need to be specified in square brackets, separated by commas.

For each model, a new model class for the revisions will be created automatically, linked to the original model class and to Revision. The revision model class will also be stored on the original model as revision_class as well as a convenience property to retrieve the revisions in descending order of their timestamp as ordered_revisions.

class kadi.lib.revisions.schemas.RevisionSchema(*args, _internal=None, **kwargs)[source]

Bases: kadi.lib.schemas.KadiSchema

Schema to represent general revisions.

See Revision.

class kadi.lib.revisions.schemas.ObjectRevisionSchema(schema=None, api_endpoint=None, view_endpoint=None, endpoint_args=None, view_object_url=None, **kwargs)[source]

Bases: kadi.lib.schemas.KadiSchema

Schema to represent specific object revisions.

See Revision.

Parameters
  • schema – (optional) The schema to represent the revisioned object with.

  • api_endpoint – (optional) An API endpoint to get the current object revision.

  • view_endpoint – (optional) An endpoint to view the current object revision. Only relevant for internal usage.

  • endpoint_args – (optional) Additional keyword arguments to append to the API and/or view endpoints when building the corresponding URL.

  • view_object_url – (optional) A URL to view the actual object the current revision refers to. Only relevant for internal usage.

kadi.lib.revisions.utils.get_revision_columns(model)[source]

Parse the list of revisioned columns for a specific model.

See also kadi.lib.revisions.core.setup_revisions().

Parameters

model – The model to get the columns from.

Returns

A tuple containing a list of the simple columns (i.e. no relationships) as string and a list of relationships. The second list itself is made up of tuples consisting of the name of the relationship and a list of colums of the relationship to revision as strings, similar to the first list.

Schemas

class kadi.lib.schemas.KadiSchema(*args, _internal=None, **kwargs)[source]

Bases: marshmallow.schema.Schema

Base class for all schemas.

Parameters

_internal – (optional) Flag indicating whether additional data that’s only relevant for internal usage should be included when serializing objects. If not set, the value returned by kadi.lib.api.utils.is_internal_api_request() will be taken instead. Note that this flag will generally not get passed automatically to any nested schemas.

load_or_400(data=None)[source]

Try to deserialize the given input.

Will try to deserialize/load the given input data using the schemas load method. If the validation fails, automatically abort the current request with status code 400 and the corresponding error response as JSON.

Parameters

data – (optional) The input to deserialize. Defaults to the JSON body of the current request.

Returns

The deserialized input.

class kadi.lib.schemas.NonEmptyString(*args, filters=None, **kwargs)[source]

Bases: marshmallow.fields.String

String field that does not allow empty or whitespace only strings.

Additionally allows to set custom filter functions that can be used to further convert the output for deserialization.

Parameters

filters – (optional) List of filter/conversion functions.

class kadi.lib.schemas.SortedPluck(nested: Union[marshmallow.base.SchemaABC, type, str, Callable[[], marshmallow.base.SchemaABC]], field_name: str, **kwargs)[source]

Bases: marshmallow.fields.Pluck

Pluck field that sorts its serialized output in case many is set.

class kadi.lib.schemas.ValidateUUID(version=4)[source]

Bases: object

Validate a UUID of a specific version in a schema field.

Parameters

version – (optional) The UUID version.

kadi.lib.schemas.validate_identifier(value)[source]

Validate an identifier in a schema field.

Uses kadi.lib.validation.validate_identifier().

Parameters

value – The field value.

kadi.lib.schemas.validate_mimetype(value)[source]

Validate a MIME type in a schema field.

Uses kadi.lib.validation.validate_mimetype().

Parameters

value – The field value.

kadi.lib.schemas.check_duplicate_identifier(data, model, exclude=None)[source]

Check for a duplicate identifier in a schema.

Has to be called manually after the usual validation as further arguments are needed for this check.

Parameters
  • data – The schema’s data.

  • model – The model the check the identifier in.

  • exclude – (optional) An object that should be excluded in the check.

Search

class kadi.lib.search.core.MappingMixin[source]

Bases: object

Mixin for custom search mappings.

classmethod get_attributes()[source]

Get a list of all attributes from a mapping class.

classmethod create_document(obj)[source]

Create a new document to be indexed in ElasticSearch

Parameters

obj – The object to be indexed.

Returns

The created document.

class kadi.lib.search.core.SearchableMixin[source]

Bases: object

Mixin for SQLALchemy models to add support for searching.

The columns to index have to be specified in a mapping class, which has to be configured with its fully qualified name using Meta.search_mapping.

Example:

class Foo:
    class Meta:
        search_mapping = "kadi.modules.record.mapping.RecordMapping"

After calling register_search_listeners(), the search index will automatically get updated whenever an object is created or deleted or if any of the indexed columns (or the state column, if present) are updated using add_to_index() and remove_from_index().

classmethod get_mapping_class()[source]

Convenience method to get the mapping class of a model.

classmethod search(query=None, sort='_score', filter_ids=None, start=0, end=10)[source]

Query the search index corresponding to this model.

Uses search_index(), but returns the actual results instead of the raw search response.

Parameters
Returns

A tuple containing a list of the search results and the total amount of hits.

Raises

elasticsearch.exceptions.ConnectionError – If no connection could be established to Elasticsearch.

classmethod register_search_listeners()[source]

Register listeners to automatically update the search index.

Uses SQLAlchemy’s before_flush, after_commit and after_rollback events.

kadi.lib.search.core.create_index(model, force=False)[source]

Create a new search index if it does not exist yet.

The name of the index will be in the form of "<tablename>_<timestamp>", where <tablename> depends on the given model. An alias <tablename> pointing to the actual index will also be created automatically if it does not exist yet.

Parameters
  • model – The model to create the index of. See also SearchableMixin.

  • force – (optional) Flag indicating whether a new index should be created even if one already exists. Note that if an alias already exists it will not be updated automatically to point to the new index.

Returns

The name of the newly created or existing index or None if no new index could be created.

kadi.lib.search.core.add_to_index(obj, index=None)[source]

Add an object to its corresponding search index.

Parameters
  • obj – The object to index. See also SearchableMixin.

  • index – (optional) The name of an index that should be used instead of the alias corresponding to the given object. See also create_index().

Returns

True if the object was indexed successfully, False otherwise.

kadi.lib.search.core.remove_from_index(obj, index=None)[source]

Remove an object from its corresponding search index.

Parameters
  • obj – The object to remove from the index. See also SearchableMixin.

  • index – (optional) The name of an index that should be used instead of the alias corresponding to the given object. See also create_index().

Returns

True if the object was removed successfully, False otherwise.

kadi.lib.search.core.search_index(index, query=None, sort='_score', filter_ids=None, start=0, end=10)[source]

Query a specific search index.

Parameters
  • index – The name of the search index.

  • query – (optional) The search query in form of an Elasticsearch DSL query.

  • sort – (optional) The name of a field or a list of multiple fields to sort on.

  • filter_ids – (optional) A list of IDs to restrict the search results to.

  • start – (optional) The start index of the results to return.

  • end – (optional) The end index of the results to return.

Returns

A response object as returned by the Elasticsearch DSL or None if the request returned an error.

Raises

elasticsearch.exceptions.ConnectionError – If no connection could be established to Elasticsearch.

class kadi.lib.search.elasticsearch.Elasticsearch(app=None)[source]

Bases: object

Elasticsearch client for use in a Flask application.

Wraps the official client for ease of use in a Flask application. Requires an application context, as it uses the application’s configuration value ELASTICSEARCH_HOSTS to specifiy one or more Elasticsearch nodes to connect to and optionally ELASTICSEARCH_CONFIG for any further configuration.

Parameters

app – (optional) The application object.

init_app(app)[source]

Initialize the application’s configuration.

Will initialize ELASTICSEARCH_HOSTS to "http://localhost:9200", ELASTICSEARCH_CONFIG to {} and ELASTICSEARCH_ENABLE_FALLBACK to False in the application’s configuration per default.

Parameters

app – The application object.

Storage

class kadi.lib.storage.core.BaseStorage(max_size=None)[source]

Bases: abc.ABC

Base class for all storage providers.

Parameters

max_size – (optional) The maximum file size for the storage to accept.

kadi.lib.storage.core.create_storage(storage_type='local', **kwargs)[source]

Create a storage provider based on a given storage type.

Parameters
  • storage_type – (optional) The type of storage provider to create.

  • **kwargs – Additional keyword arguments to pass on to the storage provider.

Returns

The new storage provider instance or None if the given storage type is invalid.

kadi.lib.storage.core.create_filepath(file_identifier, storage_type='local')[source]

Create a path from a file identifier suitable for storing files.

The structure of the path is dependent on the given storage type.

Parameters
  • file_identifier – The identifier of the file. This should generally be a file’s internal, unique ID suitable for an actual file name, e.g. a UUID like in File.id.

  • storage_type – (optional) The type of the file’s storage.

Returns

The created file path or None if the given storage type is invalid.

class kadi.lib.storage.local.LocalStorage(max_size=None, buffer_size=1048576)[source]

Bases: kadi.lib.storage.core.BaseStorage

Storage provider that uses the local file system.

Parameters
  • max_size – (optional) See BaseStorage.

  • buffer_size – (optional) The buffer size in bytes to use in memory when reading files.

static filepath_from_name(filename, dir_len=2, num_dirs=3)[source]

Create a path from a filename.

Splits up a filename "abcdefg" into the file path "ab/cd/ef/g", assuming default argument values. Useful to avoid storing lots of files in the same directory.

Parameters
  • filename – The name of the file.

  • dir_len – (optional) Length of each directory.

  • num_dirs – (optional) Number of directories.

Returns

The file path or the original filename, if its length is smaller than or equals dir_len * num_dirs.

static remove_empty_parent_dirs(path, num_dirs=1)[source]

Remove empty parent directories given a file name.

Especially useful in tandem with filepath_from_name() to remove unneeded directories.

Parameters
  • path – The file name to use as base.

  • num_dirs – (optional) The maximum number of parent directories to remove.

exists(filepath)[source]

Check if a file exists.

Parameters

filepath – The local storage path of the file.

Returns

True if the file exists, False otherwise.

open(filepath, mode='rb', encoding=None)[source]

Open a file in a specific mode.

Parameters
  • filepath – The local storage path of the file.

  • mode – (optional) The file mode to open the file with.

  • encoding – (optional) The encoding of the file to use in text mode.

Returns

The open file object.

close(file)[source]

Close an open file.

Parameters

file – The file to close.

save(dst, file_or_src, append=False)[source]

Save a file or file-like object in a specific location.

Parameters
  • dst – The local destination storage path of the new file.

  • file_or_src – A file-like object or binary stream to save or the name of an existing file to copy instead.

  • append – (optional) Flag to indicate if an existing file should be overwritten (default) or appended to.

Raises

KadiFilesizeExceededError – If the maximum file size the storage was configured with would be exceeded when saving the file.

move(src, dst)[source]

Move a file to a specific location.

Parameters
  • src – The local source storage path of the file.

  • dst – The local destination storage path of the file.

delete(filepath)[source]

Delete a file if it exists.

Parameters

filepath – The local storage path of the file.

get_mimetype(filepath)[source]

Get the MIME type of a file.

Will determine the MIME type based on the given file’s content.

Parameters

filepath – The local storage path of the file.

Returns

The MIME type of the file.

get_size(filepath)[source]

Get the size of a file.

Parameters

filepath – The local storage path of the file.

Returns

The size of the file in bytes.

validate_size(filepath, size, op='==')[source]

Validate the size of a file.

Parameters
  • filepath – The local storage path of the file.

  • size – The size to compare the file with.

  • op – (optional) The operator to use for comparison. See op in kadi.lib.utils.get_truth() for possible values.

Raises

KadiFilesizeMismatchError – If the validation failed.

get_checksum(filepath)[source]

Get the MD5 checksum of a file.

Parameters

filepath – The local storage path of the file.

Returns

The MD5 checksum as string in hex representation.

validate_checksum(filepath, expected)[source]

Validate the checksum of a file.

Parameters
  • filepath – The local storage path of the file.

  • expected – The excepted checksum as string in hex representation.

Raises

KadiChecksumMismatchError – If the checksums did not match.

kadi.lib.storage.misc.create_misc_uploads_path(filename)[source]

Create a path from a filename suitable for storing miscellaneous uploads.

Will use the local path set in MISC_UPLOADS_PATH in the application’s configuration as base storage path.

Parameters

filename – The name of the file.

kadi.lib.storage.misc.save_as_thumbnail(image_name, file_object, max_image_size=(512, 512))[source]

Save an image file as JPEG thumbnail.

Will use the local path set in MISC_UPLOADS_PATH in the application’s configuration as base storage path.

Parameters
  • image_name – The unique identifier of the thumbnail.

  • file_object – The image file object.

  • max_image_size – (optional) The maximum size of the thumbnail.

Returns

True if the thumbnail was saved successfully, False otherwise.

kadi.lib.storage.misc.delete_thumbnail(image_name)[source]

Delete a thumbnail.

This is the inverse operation of save_as_thumbnail().

Parameters

image_name – See save_as_thumbnail().

Tags

class kadi.lib.tags.core.TaggingMixin[source]

Bases: object

Mixin for SQLALchemy models to add support for tagging.

The model needs a many-to-many tags relationship connecting itself with the Tag model.

set_tags(names)[source]

Set one or more tags.

Will create a new tag object for each tag name that does not yet exist in the database and add it to the relationship. Existing tags from the relationship that are not in the given list are removed.

Parameters

names – A list of tag names.

Returns

True if the tags were set successfully, False otherwise.

class kadi.lib.tags.schemas.TagSchema(*args, _internal=None, **kwargs)[source]

Bases: kadi.lib.schemas.KadiSchema

Schema to represent tags.

See Tag.

Tasks

kadi.lib.tasks.core.launch_task(name, args=(), kwargs=None, user=None, keep=False, notify=False)[source]

Launch a new celery task by name.

Note that this function issues one or more database commits if keep is True.

Parameters
  • name – The name of the task.

  • args – (optional) Positional arguments to pass to the task as tuple.

  • kwargs – (optional) Keyword arguments to pass to the task as dictionary.

  • user – (optional) The user that started the task.

  • keep – (optional) Flag indicating whether the task should be kept in the database. See also Task.

  • notify – (optional) Flag indicating whether the user that started the task should be notified about its status, in which case a new instance of Notification of type "task_status" will be created for that user. This requires that a valid user is passed and keep must be True as well.

Returns

A boolean indicating whether the task was launched successfully or not if keep is False. A new Task object or None if keep is True.

Utils

class kadi.lib.utils.SimpleReprMixin[source]

Bases: object

Mixin to add a simple implementation of __repr__ to a class.

The provided implementation uses all instance or class attributes specified in the Meta.representation attribute of the inheriting class. It should be a list of strings specifying the attributes to use in the representation.

Example:

class Foo:
    class Meta:
        representation = ["bar", "baz"]

    bar = 1

    baz = 2
kadi.lib.utils.named_tuple(tuple_name, **kwargs)[source]

Convenience function to build a namedtuple from keyword arguments.

Parameters
  • tuple_name – The name of the tuple.

  • **kwargs – The keys and values of the tuple.

Returns

The namedtuple instance.

kadi.lib.utils.find_dict_in_list(dict_list, key, value)[source]

Find a dictionary with a specific key and value in a list.

Parameters
  • dict_list – A list of dictionaries to search.

  • key – The key to search for.

  • value – The value to search for.

Returns

The dictionary or None if it was not found.

kadi.lib.utils.get_truth(left, op, right)[source]

Compare two values with a given operator.

Parameters
  • left – The left value.

  • op – One of "==", "!=", ">", "<", ">=" or "<=".

  • right – The right value.

Returns

The boolean result of the comparison.

kadi.lib.utils.rgetattr(obj, name, default=None)[source]

Get a nested attribute of an object.

Parameters
  • obj – The object to get the attribute from.

  • name – The name of the attribute in the form of "foo.bar.baz".

  • default – (optional) The default value to return if the attribute could not be found.

Returns

The attribute or the default value if it could not be found.

kadi.lib.utils.get_class_by_name(name)[source]

Get a class given its name.

Parameters

name – The complete name of the class in the form of "foo.bar.Baz".

Returns

The class or None if it could not be found.

kadi.lib.utils.is_special_float(value)[source]

Check if a float value is a special value, i.e. nan or inf.

Parameters

value – The float value to check.

Returns

True if the value is a special float value, False otherwise.

kadi.lib.utils.is_iterable(value, include_string=False)[source]

Check if a value is an iterable.

Parameters
  • value – The value to check.

  • include_string – (optional) Flag indicating whether a string value should be treated as a valid iterable or not.

Returns

True if the value is iterable, False otherwise.

kadi.lib.utils.utcnow()[source]

Create a timezone aware datetime object of the current time in UTC.

Returns

A datetime object as specified in Python’s datetime module.

kadi.lib.utils.random_alnum(length=16)[source]

Generate a (cryprographically secure) random alphanumeric string.

Parameters

length – (optional) The length of the string.

Returns

The generated string.

kadi.lib.utils.compact_json(data)[source]

Serialize data to a JSON formatted string without any unnecessary spaces.

Parameters

data – The data to serialize.

Returns

The JSON formatted string.

kadi.lib.utils.signal_resource_change(resource, user=None, created=False)[source]

Helper function to run the kadi_post_resource_change plugin hook.

Generally, it is supposed to be run after a resource was created or changed and the change triggered the creation of a new revision.

Parameters
  • resource – The resource that was created or changed.

  • user – (optional) The user that triggered the creation or change. Defaults to the current user.

  • created – (optional) Flag indicating if the resource was newly created.

Validation

kadi.lib.validation.validator(exception_class)[source]

Decorator to wrap a validation function.

Handles errors of type KadiValidationError and reraises another customizable exception using the same message.

Parameters

exception_class – The exception to raise in case of validation failure.

kadi.lib.validation.validate_uuid(uuid_string, version=4)[source]

Validate a string agains a specific UUID version.

Parameters
  • uuid_string – The UUID string.

  • version – (optional) The UUID version.

Raises

KadiValidationError – If the validation fails.

kadi.lib.validation.validate_identifier(value)[source]

Validate the format of an “identifier”.

Identifiers can be used to give resources a human-readable, unique identification. An identifier is restricted to lowercase alphanumeric characters, hyphens and underscores.

Parameters

value – The identifier string.

Raises

KadiValidationError – If the validation fails.

kadi.lib.validation.validate_mimetype(value)[source]

Validate format of a MIME type.

A MIME type has to start with at least one alphabetical character, followed by a forward slash, followed by lowercase alphanumeric characters or the special characters "-", "+" or ".".

Parameters

value – The MIME type string.

Raises

KadiValidationError – If the validation fails.

kadi.lib.validation.validate_username(value)[source]

Validate format of a local username.

Local usernames are restricted to lowercase alphanumeric characters with single hyphens in between.

Parameters

value – The username string.

Raises

KadiValidationError – If the validation fails.

Web

class kadi.lib.web.IdentifierConverter(map: werkzeug.routing.Map, *args: Any, **kwargs: Any)[source]

Bases: werkzeug.routing.BaseConverter

Custom URL converter for identifiers.

Automatically uses the same conversions that are applied when creating or updating an identifier. See also kadi.lib.validation.validate_identifier().

kadi.lib.web.paginated(page_max=None, per_page_max=100)[source]

Decorator to parse paginated query parameters.

Convenience decorator to get and parse the query parameters "page" and "per_page" from the current request. The former defaults to 1 while the latter defaults to 10 if no valid integer values were found. Both parameters will be injected into the decorated function as keyword arguments page and per_page.

The query parameters are also used when generating the API documentation.

Parameters
  • page_max – (optional) The maximum possible value of the "page" parameter.

  • per_page_max – (optional) The maximum possible value of the "per_page" parameter.

kadi.lib.web.qparam(name, location=None, multiple=False, default='', parse=None, description='')[source]

Decorator to parse a query parameter.

Convenience decorator to retrieve and parse a specified query parameter from the current request. The decorator can be applied multiple times. Each parameter will be injected into the decorated function as part a dictionary inside the keyword argument qparams. The dictionary maps each given parameter name to its respective value.

The query parameter is also used when generating the API documentation.

Parameters
  • name – The name of both the query parameter and the dictionary key that is injected into the decorated function.

  • location – (optional) The name of the query parameter to use instead of name.

  • multiple – (optional) Flag indicating whether the query parameter can be specified multiple times and should be retrieved as list value.

  • default – (optional) The default value or a callable returning a default value to use in case the query parameter is missing and multiple is False, otherwise the default value will always be an empty list.

  • parse – (optional) A callable or list of callables to parse the parameter value if it is not missing. Each callable must take and return a single parameter value. If parsing fails with a ValueError, the default value is taken instead if multiple is False, otherwise each invalid value is removed from the resulting list.

  • description – (optional) A description of the query parameter, which is only used when generating the API documentation. Supports reST syntax.

kadi.lib.web.url_for(endpoint, _ignore_version=False, **values)[source]

Generate an URL based on a given endpoint.

Wraps Flask’s url_for function with additional support for generating the correct URLs when using API versioning. Additionally, generated URLs are always external, i.e. absolute.

Parameters
  • endpoint – The endpoint (name of the function) of the URL.

  • _ignore_version – (optional) Flag indicating whether the API version should be ignored when building the URL in API requests.

  • **values – The variable arguments of the URL rule.

Returns

The generated URL string.

kadi.lib.web.static_url(filename)[source]

Generate a static URL for a given filename.

Will make use of the MANIFEST_MAPPING if it is defined in the application’s configuration and if an entry exists for the given filename.

Parameters

filename – The name of the file to include in the URL.

Returns

The generated URL string.

kadi.lib.web.download_stream(data, download_name, as_attachment=True, mimetype=None)[source]

Send a binary stream as file to a client.

Parameters
  • data – The in-memory binary stream to send.

  • download_name – The default name the browser should use when downloading the file.

  • as_attachment – (optional) Flag indicating whether the file should be sent as an attachment instead of displaying it in the browser.

  • mimetype – (optional) The MIME type of the file. Defaults to a MIME type based on the given filename or "application/octet-stream" if it cannot be guessed.

Returns

The response object.

kadi.lib.web.download_string(data, download_name, as_attachment=True, mimetype=None)[source]

Send UTF-8 encoded text data as file to a client.

Parameters
Returns

The response object.

kadi.lib.web.get_locale()[source]

Get the current locale.

The locale query parameter of the current request will take precedence, followed by the locale cookie as configured by LOCALE_COOKIE_NAME in the application’s configuration and finally the default locale. The chosen locale has to be valid, i.e. it has to be configured in the application’s configuration in LOCALES.

Returns

The current locale. If no valid locale could be found, LOCALE_DEFAULT will be returned as configured in the application’s configuration.

kadi.lib.web.make_next_url(next_url)[source]

Create a target URL to redirect a user to after login.

Parameters

next_url – An internal URL to redirect to.

kadi.lib.web.get_next_url(fallback=None)[source]

Get the validated target URL to redirect a user to after login.

The target URL has to be specified as a next query parameter in the current request.

Parameters

fallback – (optional) The fallback URL to use in case the target URL was invalid or could not be found. Defaults to the index page.

kadi.lib.web.get_error_message(status_code)[source]

Get an error message corresponding to an HTTP status code.

Parameters

status_code – The HTTP status code.

Returns

The error message.

kadi.lib.web.get_error_description(status_code)[source]

Get an error description corresponding to an HTTP status code.

Parameters

status_code – The HTTP status code.

Returns

The error description.