Source code for vulyk.utils

# -*- coding: utf-8 -*-
"""Every project must have a package called `utils`."""
import os
import sys
from http import HTTPStatus
from itertools import islice
from typing import Iterator, Optional, Dict, Generator, Tuple

import flask
from flask import abort, Response

try:
    import ujson as json
except ImportError:
    import json

from vulyk.models.user import User

__all__ = [
    'chunked',
    'get_tb',
    'get_template_path',
    'json_response',
    'NO_TASKS',
    'resolve_task_type'
]


[docs]def resolve_task_type(type_id: str, tasks: Dict, user: User): """ Looks for `type_id` in TASK_TYPES map. :param type_id: ID of the TaskType in the map. :type type_id: str :param tasks: map of `task type id -> task type instance` :type tasks: Dict[str, vulyk.models.task_types.AbstractTaskType] :param user: Current user. :type user: User :returns: Correct TaskType instance or throws an exception. :rtype: vulyk.models.task_types.AbstractTaskType """ task_type = None if not (type_id and type_id in tasks.keys()): abort(HTTPStatus.NOT_FOUND) elif not user.is_eligible_for(type_id): abort(HTTPStatus.FORBIDDEN) else: task_type = tasks[type_id] return task_type
# Borrowed from elasticutils
[docs]def chunked(iterable: Iterator, n: int) -> Generator[Tuple, None, None]: """Returns chunks of n length of iterable If len(iterable) % n != 0, then the last chunk will have length less than n. Example: >>> chunked([1, 2, 3, 4, 5], 2) [(1, 2), (3, 4), (5,)] :param iterable: Source we need to chop up. :type iterable: Iterator :param n: Slice length :type n: int :returns: Sequence of tuples of given size. :rtype: Generator[Tuple, None, None] """ iterable = iter(iterable) while 1: t = tuple(islice(iterable, n)) if t: yield t else: return
[docs]def get_tb() -> Dict: """ Returns traceback of the latest exception caught in 'except' block :return: traceback of the most recent exception """ return sys.exc_info()[2]
[docs]def get_template_path(app: flask.Flask, name: str) -> str: """ Finds the path to the template. :param app: Flask application instance. :type app: flask.Flask :param name: Name of the template. :type name: str :return: Full path to the template. :rtype: str """ for x in app.jinja_loader.list_templates(): for folder in app.config.get('TEMPLATE_BASE_FOLDERS', []): if folder and os.path.join(folder, 'base', name) == x: return x return 'base/%s' % name
[docs]def json_response(result: Dict, errors: Optional[Iterator] = None, status: int = HTTPStatus.OK) -> Response: """ Handy helper to prepare unified responses. :param result: Data to be sent :type result: Dict :param errors: List (set, tuple, dict) of errors :type errors: Optional[Iterator] :param status: Response http-status :type status: int :returns: Jsonified response :rtype: flask.Response """ if not errors: errors = [] data = json.dumps({ 'result': result, 'errors': errors}) return flask.Response( data, status, mimetype='application/json', headers=[ ('Cache-Control', 'no-cache, no-store, must-revalidate'), ('Pragma', 'no-cache'), ('Expires', '0'), ])
NO_TASKS = json_response({}, ['There is no task having type like this'], HTTPStatus.NOT_FOUND)