Example #1
0
                'type': 'array',
                'items': {
                    'type': 'string'
                }
            },
            'phase_handlers': {
                'type': 'array',
                'items': phase_object
            }
        }
    }

    plugin_list_reply = {'type': 'array', 'items': plugin_object}


plugin_schema = api.schema('plugin_object', ObjectsContainer.plugin_object)
plugin_list_reply_schema = api.schema('plugin_list_reply',
                                      ObjectsContainer.plugin_list_reply)

plugin_parser = api.parser()
plugin_parser.add_argument(
    'include_schema',
    type=inputs.boolean,
    default=False,
    help='Include plugin schema. This will increase response size')

plugins_parser = api.pagination_parser(plugin_parser)

plugins_parser.add_argument('interface',
                            case_sensitive=False,
                            help='Show plugins which implement this interface')
Example #2
0
    'type': 'object',
    'properties': {
        'code': {
            'type': 'integer'
        },
        'error': {
            'type': 'array',
            'items': config_error
        },
        'message': {
            'type': 'string'
        },
    }
}

yaml_error_schema = api.schema('yaml_error_schema', yaml_error_response)
config_validation_schema = api.schema('config_validation_schema',
                                      config_validation_error)


@server_api.route('/reload/')
class ServerReloadAPI(APIResource):
    @api.response(501,
                  model=yaml_error_schema,
                  description='YAML syntax error')
    @api.response(502,
                  model=config_validation_schema,
                  description='Config validation error')
    @api.response(200, description='Newly reloaded config')
    def get(self, session=None):
        """ Reload Flexget config """
Example #3
0
    def get(self, session=None):
        """ Reload Flexget config """
        log.info('Reloading config from disk.')
        try:
            self.manager.load_config()
        except ValueError as e:
            raise ApiError('Error loading config: %s' % e.args[0])

        log.info('Config successfully reloaded from disk.')
        return {}


pid_schema = api.schema('server.pid', {
    'type': 'object',
    'properties': {
        'pid': {
            'type': 'integer'
        }
    }
})


@server_api.route('/pid/')
class ServerPIDAPI(APIResource):
    @api.response(200, description='Reloaded config', model=pid_schema)
    def get(self, session=None):
        """ Get server PID """
        return {'pid': os.getpid()}


shutdown_parser = api.parser()
shutdown_parser.add_argument('force',
Example #4
0
            request.path.startswith('/logout') or request.path.startswith('/swagger'):
        return

    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()


# API Authentication and Authorization
login_api = api.namespace('login', description='API Authentication')

login_api_schema = api.schema(
    'login', {
        'type': 'object',
        'properties': {
            'username': {
                'type': 'string'
            },
            'password': {
                'type': 'string'
            }
        }
    })

login_parser = api.parser()
login_parser.add_argument('remember',
                          type=bool,
                          required=False,
                          default=False,
                          help='Remember for next time')


@login_api.route('/')
Example #5
0
movie_queue_api = api.namespace('movie_queue',
                                description='Movie Queue operations')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {
            'type': 'string'
        },
        'message': {
            'type': 'string'
        }
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

empty_response = api.schema('empty', {'type': 'object'})

movie_object = {
    'type': 'object',
    'properties': {
        'added_date': {
            'type': 'string'
        },
        'is_downloaded': {
            'type': 'boolean'
        },
        'download_date': {
            'type': 'string'
        },
Example #6
0
            'last_update': {
                'type': 'string',
                'format': 'date-time'
            }
        },
        'required': [
            'tvmaze_id', 'series_id', 'number', 'season_number', 'title',
            'airdate', 'url', 'original_image', 'medium_image', 'airstamp',
            'runtime', 'summary', 'last_update'
        ],
        'additionalProperties':
        False
    }


tvmaze_series_schema = api.schema('tvmaze_series_schema',
                                  ObjectsContainer.tvmaze_series_object)
tvmaze_episode_schema = api.schema('tvmaze_episode_schema',
                                   ObjectsContainer.tvmaze_episode_object)


@tvmaze_api.route('/series/<string:title>/')
@api.doc(params={'title': 'TV Show name or TVMaze ID'})
class TVDBSeriesSearchApi(APIResource):
    @etag
    @api.response(200, 'Successfully found show', model=tvmaze_series_schema)
    @api.response(NotFoundError)
    def get(self, title, session=None):
        """TVMaze series lookup"""
        try:
            tvmaze_id = int(title)
        except ValueError:
Example #7
0
        raise ValueError('Value expected to be in' + ' ,'.join(enum))
    return value


def movie_queue_sort_order_enum(value):
    enum = ['desc', 'asc']
    if isinstance(value, bool):
        return value
    if value not in enum:
        raise ValueError('Value expected to be in' + ' ,'.join(enum))
    if value == 'desc':
        return True
    return False


movie_queue_schema = api.schema('list_movie_queue', movie_queue_schema)

movie_queue_parser = api.parser()
movie_queue_parser.add_argument('page', type=int, default=1, help='Page number')
movie_queue_parser.add_argument('max', type=int, default=100, help='Movies per page')
movie_queue_parser.add_argument('status', type=movie_queue_status_value_enum, default=False,
                                help='Filter list by status. Filter by {0}. Default is "pending"'.format(
                                        ' ,'.join(movie_queue_status_value_enum_list)))
movie_queue_parser.add_argument('sort_by', type=movie_queue_sort_value_enum, default='added',
                                help="Sort response by 'added', 'downloaded', 'id', 'title'")
movie_queue_parser.add_argument('order', type=movie_queue_sort_order_enum, default='desc',
                                help="Sorting order, can be 'asc' or 'desc'")

movie_add_results_schema = {
    'type': 'object',
    'properties': {
Example #8
0
from flask import jsonify

from flexget.db_schema import reset_schema, plugin_schemas
from flexget.api import api, APIResource
from flexget.api.app import base_message_schema, success_response, BadRequest, etag

log = logging.getLogger('database')

db_api = api.namespace('database', description='Manage Flexget DB')


class ObjectsContainer(object):
    plugin_list = {'type': 'array', 'items': {'type': 'string'}}


plugins_schema = api.schema('plugins_list', ObjectsContainer.plugin_list)


@db_api.route('/cleanup/')
class DBCleanup(APIResource):
    @etag
    @api.response(200, model=base_message_schema)
    def get(self, session=None):
        """ Make all plugins clean un-needed data from the database """
        self.manager.db_cleanup(force=True)
        return success_response('DB Cleanup triggered')


@db_api.route('/vacuum/')
class DBVacuum(APIResource):
    @etag
Example #9
0
        'error': {'type': 'string'},
        'config_path': {'type': 'string'}
    }
}

config_validation_error = {
    'type': 'object',
    'properties': {
        'code': {'type': 'integer'},
        'error': {'type': 'array', 'items': config_error},
        'message': {'type': 'string'},

    }
}

yaml_error_schema = api.schema('yaml_error_schema', yaml_error_response)
config_validation_schema = api.schema('config_validation_schema', config_validation_error)


@server_api.route('/reload/')
class ServerReloadAPI(APIResource):
    @api.response(501, model=yaml_error_schema, description='YAML syntax error')
    @api.response(502, model=config_validation_schema, description='Config validation error')
    @api.response(200, description='Newly reloaded config')
    def get(self, session=None):
        """ Reload Flexget config """
        log.info('Reloading config from disk.')
        try:
            self.manager.load_config(output_to_console=False)
        except YAMLError as e:
            if hasattr(e, 'problem') and hasattr(e, 'context_mark') and hasattr(e, 'problem_mark'):
Example #10
0
                'type': 'string'
            }
        }
    }

    connection = {
        'type': 'object',
        'patternProperties': {
            '\w': connection_object
        }
    }

    return_response = {'type': 'array', 'items': connection}


return_schema = api.schema('irc.connections', ObjectsContainer.return_response)


@irc_api.route('/connections/')
@api.doc(parser=irc_parser)
class IRCStatus(APIResource):
    @api.response(200, model=return_schema)
    @api.response(NotFoundError)
    @api.response(BadRequest)
    def get(self, session=None):
        """Returns status of IRC connections"""
        from flexget.plugins.daemon.irc import irc_manager
        if irc_manager is None:
            raise BadRequest('IRC daemon does not appear to be running')

        args = irc_parser.parse_args()
Example #11
0
from builtins import *

from flask import jsonify, request
from jsonschema import RefResolutionError

from flexget.api import api, APIResource
from flexget.config_schema import schema_paths, resolve_ref

schema_api = api.namespace('schema', description='Config and plugin schemas')
_plugins_cache = None

schema_api_list = api.schema('schema.list', {
    'type': 'object',
    'properties': {
        'schemas': {
            'type': 'array',
            'items': {'type': 'object'}
        }
    }
})


@schema_api.route('/')
class SchemaAllAPI(APIResource):
    @api.response(200, model=schema_api_list)
    def get(self, session=None):
        """ List all schema definitions """
        schemas = {}
        for path in schema_paths:
            schemas[path] = resolve_ref(path)
Example #12
0
PLUGIN_TASK_NAME = 'seen_plugin_API'  # Name of task to use when adding entries via API

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {
            'type': 'string'
        },
        'message': {
            'type': 'string'
        }
    }
}

empty_response = api.schema('empty', {'type': 'object'})

default_error_schema = api.schema('default_error_schema', default_error_schema)

seen_field_object = {
    'type': 'object',
    'properties': {
        'id': {
            'type': 'integer'
        },
        'field': {
            'type': 'string'
        },
        'value': {
            'type': 'string'
        },
Example #13
0
                'type': 'integer'
            }
        }
    }
    search_results_object = {
        'type': 'object',
        'properties': {
            'search_results': {
                'type': 'array',
                'items': search_result_object
            }
        }
    }


default_error_schema = api.schema('default_error_schema',
                                  objects_container.default_error_schema)
tvdb_series_schema = api.schema('tvdb_series_schema',
                                objects_container.tvdb_series_object)
tvdb_episode_schema = api.schema('tvdb_episode_schema',
                                 objects_container.episode_object)
search_results_schema = api.schema('tvdb_search_results_schema',
                                   objects_container.search_results_object)

series_parser = api.parser()
series_parser.add_argument('include_actors',
                           type=inputs.boolean,
                           help='Include actors in response')


@tvdb_api.route('/series/<string:title>/')
@api.doc(params={'title': 'TV Show name or TVDB ID'}, parser=series_parser)
Example #14
0
                'format': 'file'
            },
            'path': {
                'type': 'string',
                'format': 'path'
            },
            'url': {
                'type': 'string',
                'format': 'url'
            },
            'episode_identifier': {
                'type': 'string',
                'format': 'episode_identifier'
            }
        }
    }


format_checker_schema = api.schema('format_checker',
                                   ObjectContainer.format_checker_input)


@schema_api.route('/', doc=False)
class SchemaTest(APIResource):
    @api.validate(format_checker_schema)
    @api.response(200, model=base_message_schema)
    def post(self, session=None):
        """ Validate flexget custom schema"""
        # If validation passed, all is well
        return success_response('payload is valid')
Example #15
0
@event("manager.shutdown")
def stop_scheduler(manager):
    if scheduler and scheduler.running:
        scheduler.shutdown(wait=False)


@event("config.register")
def register_config():
    register_config_key("schedules", main_schema)
    register_schema("/schema/config/schedule", schedule_schema)


schedule_api = api.namespace("schedules", description="Task Scheduler")

api_schedule_schema = api.schema("schedule", schedule_schema)
api_schedules_list_schema = api.schema("schedule", main_schema)


def _schedule_by_id(schedule_id):
    for schedule in manager.config.get("schedules", []):
        if id(schedule) == schedule_id:
            schedule = schedule.copy()
            schedule["id"] = schedule_id
            return schedule


@schedule_api.route("/")
class SchedulesAPI(APIResource):
    @api.response(200, "List of schedules", api_schedules_list_schema)
    def get(self, session=None):
Example #16
0
            'network': {'type': 'string'},
            'series_name': {'type': 'string'},
            'status': {'type': 'string'},
            'overview': {'type': 'string'},
            'tvdb_id': {'type': 'integer'}
        }
    }
    search_results_object = {
        'type': 'object',
        'properties': {
            'search_results': {'type': 'array', 'items': search_result_object}
        }
    }


default_error_schema = api.schema('default_error_schema', objects_container.default_error_schema)
tvdb_series_schema = api.schema('tvdb_series_schema', objects_container.tvdb_series_object)
tvdb_episode_schema = api.schema('tvdb_episode_schema', objects_container.episode_object)
search_results_schema = api.schema('tvdb_search_results_schema', objects_container.search_results_object)

series_parser = api.parser()
series_parser.add_argument('include_actors', type=inputs.boolean, help='Include actors in response')


@tvdb_api.route('/series/<string:title>/')
@api.doc(params={'title': 'TV Show name or TVDB ID'}, parser=series_parser)
class TVDBSeriesSearchApi(APIResource):

    @api.response(200, 'Successfully found show', tvdb_series_schema)
    @api.response(404, 'No show found', default_error_schema)
    def get(self, title, session=None):
Example #17
0
from flexget.api import api, APIResource
from flexget.api.app import NotFoundError, APIError, base_message_schema, success_response, etag, Conflict

schedule_api = api.namespace('schedules', description='Task Scheduler')


class ObjectsContainer(object):
    # SwaggerUI does not yet support anyOf or oneOf
    schedule_object = copy.deepcopy(schedule_schema)
    schedule_object['properties']['id'] = {'type': 'integer'}
    schedule_object['maxProperties'] += 1

    schedules_list = {'type': 'array', 'items': schedule_object}


base_schedule_schema = api.schema('schedules.base', schedule_schema)
api_schedule_schema = api.schema('schedules.schedule', ObjectsContainer.schedule_object)
api_schedules_list_schema = api.schema('schedules.list', ObjectsContainer.schedules_list)


def _schedule_by_id(schedule_id, schedules):
    for idx, schedule in enumerate(schedules):
        if schedule and id(schedule) == schedule_id:
            schedule = schedule.copy()
            schedule['id'] = schedule_id
            return schedule, idx
    return None, None


schedule_desc = "Schedule ID changes upon daemon restart. The schedules object supports either interval or schedule" \
                " (cron) objects, see the model definition for details. Tasks also support string or list " \
Example #18
0
        'category': {'type': 'string'},
        'contexts': {'type': 'array', 'items': {'type': 'string'}},
        'debug': {'type': 'boolean'},
        'groups': {'type': 'array', 'items': {'type': 'string'}},
        'phase_handlers': {'type': 'array', 'items': phase_object}
    }
}

plugin_list_reply = {
    'type': 'object',
    'properties': {
        'plugin_list': {'type': 'array', 'items': plugin_object},
        'number_of_plugins': {'type': 'integer'}
    }
}
plugin_schema = api.schema('plugin_object', plugin_object)
plugin_list_reply_schema = api.schema('plugin_list_reply', plugin_list_reply)

plugin_parser = api.parser()
plugin_parser.add_argument('include_schema', type=inputs.boolean, default=False,
                           help='Include plugin schema. This will increase response size')

plugins_parser = plugin_parser.copy()

plugins_parser.add_argument('group', help='Show plugins belonging to this group')
plugins_parser.add_argument('phase', help='Show plugins that act on this phase')


def plugin_to_dict(plugin):
    """ Returns a dict for API usage from a PluginInfo object
    :param plugin: PluginInfo instance
Example #19
0
    },
    'additionalProperties': False
}

tasks_api_schema = {
    "type": "object",
    "properties": {
        "tasks": {
            "type": "array",
            "items": task_api_schema
        }
    },
    'additionalProperties': False
}

tasks_api_schema = api.schema('tasks', tasks_api_schema)
task_api_schema = api.schema('task', task_api_schema)


@tasks_api.route('/')
class TasksAPI(APIResource):

    @api.response(200, 'list of tasks', tasks_api_schema)
    def get(self, session=None):
        """ Show all tasks """

        tasks = []
        for name, config in self.manager.user_config.get('tasks', {}).iteritems():
            tasks.append({'name': name, 'config': config})
        return {'tasks': tasks}
Example #20
0
    def get(self, session=None):
        """ Reload Flexget config """
        log.info('Reloading config from disk.')
        try:
            self.manager.load_config()
        except ValueError as e:
            raise ApiError('Error loading config: %s' % e.args[0])

        log.info('Config successfully reloaded from disk.')
        return {}


pid_schema = api.schema('server_pid', {
    'type': 'object',
    'properties': {
        'pid': {
            'type': 'integer'
        }
    }
})


@server_api.route('/pid/')
class ServerPIDAPI(APIResource):
    @api.response(200, 'Reloaded config', pid_schema)
    def get(self, session=None):
        """ Get server PID """
        return{'pid': os.getpid()}


shutdown_parser = api.parser()
shutdown_parser.add_argument('force', type=bool, required=False, default=False, help='Ignore tasks in the queue')
Example #21
0
from flask.ext.login import current_user

from flexget.api import api, APIResource
from flexget.webserver import change_password, generate_token, WeakPassword

user_api = api.namespace('user', description='Manage user login credentials')

user_password_input = {
    'type': 'object',
    'properties': {
        'password': {
            'type': 'string'
        }
    }
}
user_password_input_schema = api.schema('user_password_input',
                                        user_password_input)

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {
            'type': 'string'
        },
        'message': {
            'type': 'string'
        }
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)
Example #22
0
from flexget.options import get_parser
from flexget.task import task_phases
from flexget.utils import json
from flexget.utils import requests
from flexget.utils.lazy_dict import LazyLookup

# Tasks API
tasks_api = api.namespace('tasks', description='Manage Tasks')

tasks_list_api_schema = api.schema(
    'tasks.list', {
        "type": "object",
        "properties": {
            "tasks": {
                "type": "array",
                "items": {
                    '$ref': '#/definitions/tasks.task'
                }
            }
        },
        'additionalProperties': False
    })

task_schema_validate = {
    'type': 'object',
    'properties': {
        'name': {
            'type': 'string'
        },
        'config': {
            '$ref': '/schema/plugins'
Example #23
0
import flexget.plugins.list.entry_list as el
from flexget.api import api, APIResource

log = logging.getLogger('entry_list')

entry_list_api = api.namespace('entry_list', description='Entry List operations')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}
empty_response = api.schema('empty', {'type': 'object'})

default_error_schema = api.schema('default_error_schema', default_error_schema)
entry_list_base_object = {
    'type': 'object',
    'properties': {
        'id': {'type': 'integer'},
        'name': {'type': 'string'},
        'added_on': {'type': 'string'}
    }
}
entry_list_input_object = copy.deepcopy(entry_list_base_object)
del entry_list_input_object['properties']['id']
del entry_list_input_object['properties']['added_on']

entry_list_return_lists = {'type': 'array', 'items': entry_list_base_object}
Example #24
0
        'release_downloaded': {'type': 'string'},
        'release_quality': {'type': 'string'},
        'release_proper_count': {'type': 'integer'},
        'release_first_seen': {'type': 'string'},
        'release_episode_id': {'type': 'integer'}
    }
}

release_schema = {
    'type': 'object',
    'properties': {
        'episode_id': {'type': 'integer'},
        'release': release_object
    }
}
release_schema = api.schema('release_schema', release_schema)

release_list_schema = {
    'type': 'object',
    'properties': {
        'releases': {
            'type': 'array',
            'items': release_object
        },
        'number_of_releases': {'type': 'integer'},
        'episode_id': {'type': 'integer'},
        'show_id': {'type': 'integer'}
    }
}
release_list_schema = api.schema('release_list_schema', release_list_schema)
Example #25
0
    movie_object = {
        'type': 'object',
        'properties': {
            'imdb_id': {'type': 'string'},
            'match': {'type': 'number'},
            'name': {'type': 'string'},
            'url': {'type': 'string'},
            'year': {'type': 'string'},
            'thumbnail': {'type': 'string'}
        }
    }

    return_object = {'type': 'array', 'items': movie_object}


return_schema = api.schema('imdb_search_schema', ObjectsContainer.return_object)


@imdb_api.route('/search/<string:title>/')
@api.doc(params={'title': 'Movie name or IMDB ID'})
class IMDBMovieSearch(APIResource):
    @api.response(200, model=return_schema)
    def get(self, title, session=None):
        """ Get a list of IMDB search result by name or ID"""
        raw_movies = ImdbSearch().smart_match(title, single_match=False)
        if not raw_movies:
            return []
        # Convert single movie to list to preserve consistent reply
        if not isinstance(raw_movies, list):
            raw_movies = [raw_movies]
        return jsonify(raw_movies)
Example #26
0
from flask import jsonify, Response
from flexget.task import task_phases
from flexget.utils import json
from json import JSONEncoder
from flexget.event import event
from flexget.utils.lazy_dict import LazyLookup
import flask_restplus

# Tasks API
tasks_api = api.namespace('tasks', description='Manage Tasks')

tasks_list_api_schema = api.schema('tasks.list', {
    "type": "object",
    "properties": {
        "tasks": {
            "type": "array",
            "items": {'$ref': '#/definitions/tasks.task'}
        }
    },
    'additionalProperties': False
})


task_schema_validate = {
    'type': 'object',
    'properties': {
        'name': {'type': 'string'},
        'config': {'$ref': '/schema/plugins'}
    },
    'additionalProperties': False
}
Example #27
0
from flask import jsonify
from flexget.api import api, APIResource
from flexget.config_schema import schema_paths, resolve_ref

schema_api = api.namespace('schema', description='Config and plugin schemas')
_plugins_cache = None

schema_api_list = api.schema(
    'schema.list', {
        'type': 'object',
        'properties': {
            'schemas': {
                'type': 'array',
                'items': {
                    'type': 'object'
                }
            }
        }
    })


@schema_api.route('/')
class SchemaAllAPI(APIResource):
    @api.response(200, model=schema_api_list)
    def get(self, session=None):
        """ List all schema definitions """
        schemas = {}
        for path in schema_paths:
            schemas[path] = resolve_ref(path)

        return jsonify({'schemas': schemas})
Example #28
0
from flask_restplus import inputs

from flexget.api import api, APIResource
from flexget.plugins.api_tvdb import lookup_series, lookup_episode

tvdb_api = api.namespace('tvdb', description='TheTVDB Shows')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

tvdb_series_object = {
    'type': 'object',
    'properties': {
        'tvdb_id': {'type': 'integer'},
        'last_updated': {'type': 'string', 'format': 'date-time'},
        'expired': {'type': 'boolean'},
        'name': {'type': 'string'},
        'rating': {'type': 'number'},
        'status': {'type': 'string'},
        'runtime': {'type': 'integer'},
        'airs_time': {'type': 'string'},
        'airs_dayofweek': {'type': 'string'},
        'content_rating': {'type': 'string'},
        'network': {'type': 'string'},
Example #29
0
        "type": "object",
        "properties": {"id": {"type": "integer"}, "added_on": {"type": "string"}, "name": {"type": "string"}},
    }

    list_input = copy.deepcopy(list_object)
    del list_input["properties"]["id"]
    del list_input["properties"]["added_on"]

    return_movies = {"type": "array", "items": movie_list_object}

    return_lists = {"type": "array", "items": list_object}

    return_identifiers = {"type": "array", "items": {"type": "string"}}


input_movie_entry_schema = api.schema("input_movie_entry", ObjectsContainer.input_movie_entry)
input_movie_list_id_schema = api.schema("input_movie_list_id_object", ObjectsContainer.input_movie_list_id_object)

movie_list_id_object_schema = api.schema("movie_list_id_object", ObjectsContainer.return_movie_list_id_object)
movie_list_object_schema = api.schema("movie_list_object", ObjectsContainer.movie_list_object)
list_object_schema = api.schema("list_object", ObjectsContainer.list_object)
return_lists_schema = api.schema("return_lists", ObjectsContainer.return_lists)
return_movies_schema = api.schema("return_movies", ObjectsContainer.return_movies)

new_list_schema = api.schema("new_list", ObjectsContainer.list_input)
identifiers_schema = api.schema("movie_list.identifiers", ObjectsContainer.return_identifiers)

movie_list_parser = api.parser()
movie_list_parser.add_argument("name", help="Filter results by list name")

Example #30
0
from __future__ import unicode_literals, division, absolute_import
from builtins import *  # pylint: disable=unused-import, redefined-builtin

import logging

from flask import jsonify
from sqlalchemy.orm.exc import NoResultFound

from flexget.api import api, APIResource
from flexget.plugins.filter.retry_failed import FailedEntry

log = logging.getLogger('retry_failed_api')

retry_failed_api = api.namespace('retry_failed', description='View and manage failed entries')

empty_response = api.schema('empty', {'type': 'object'})

retry_failed_entry_object = {
    'type': 'object',
    'properties': {
        'id': {'type': 'integer'},
        'title': {'type': 'string'},
        'url': {'type': 'string'},
        'added_at': {'type': 'string', 'format': 'date-time'},
        'reason': {'type': 'string'},
        'count': {'type': 'integer'},
        'retry_time': {'type': 'string', 'format': 'date-time'}
    }
}
retry_entries_list_object = {
    'type': 'object',
Example #31
0
from builtins import *  # pylint: disable=unused-import, redefined-builtin

import logging

from flask import jsonify
from sqlalchemy.orm.exc import NoResultFound

from flexget.api import api, APIResource
from flexget.plugins.filter.retry_failed import FailedEntry

log = logging.getLogger('failed_api')

retry_failed_api = api.namespace('failed',
                                 description='View and manage failed entries')

empty_response = api.schema('empty', {'type': 'object'})

retry_failed_entry_object = {
    'type': 'object',
    'properties': {
        'id': {
            'type': 'integer'
        },
        'title': {
            'type': 'string'
        },
        'url': {
            'type': 'string'
        },
        'added_at': {
            'type': 'string',
Example #32
0
    pending_entry_list = {'type': 'array', 'items': pending_entry_object}

    operation_object = {
        'type': 'object',
        'properties': {
            'operation': {
                'type': 'string',
                'enum': ['approve', 'reject']
            }
        },
        'required': ['operation'],
        'additionalProperties': False
    }


pending_entry_schema = api.schema('pending.entry',
                                  ObjectsContainer.pending_entry_object)
pending_entry_list_schema = api.schema('pending.entry_list',
                                       ObjectsContainer.pending_entry_list)
operation_schema = api.schema('pending.operation',
                              ObjectsContainer.operation_object)

filter_parser = api.parser()
filter_parser.add_argument('task_name', help='Filter by task name')
filter_parser.add_argument('approved',
                           type=inputs.boolean,
                           help='Filter by approval status')

sort_choices = ('added', 'task_name', 'title', 'url', 'approved')
pending_parser = api.pagination_parser(parser=filter_parser,
                                       sort_choices=sort_choices)
Example #33
0
            }
        }
    }

    list_input = copy.deepcopy(list_object)
    del list_input['properties']['id']
    del list_input['properties']['added_on']

    return_movies = {'type': 'array', 'items': movie_list_object}

    return_lists = {'type': 'array', 'items': list_object}

    return_identifiers = {'type': 'array', 'items': {'type': 'string'}}


input_movie_entry_schema = api.schema('input_movie_entry',
                                      ObjectsContainer.input_movie_entry)
input_movie_list_id_schema = api.schema(
    'input_movie_list_id_object', ObjectsContainer.input_movie_list_id_object)

movie_list_id_object_schema = api.schema(
    'movie_list_id_object', ObjectsContainer.return_movie_list_id_object)
movie_list_object_schema = api.schema('movie_list_object',
                                      ObjectsContainer.movie_list_object)
list_object_schema = api.schema('list_object', ObjectsContainer.list_object)
return_lists_schema = api.schema('return_lists', ObjectsContainer.return_lists)
return_movies_schema = api.schema('return_movies',
                                  ObjectsContainer.return_movies)

new_list_schema = api.schema('new_list', ObjectsContainer.list_input)
identifiers_schema = api.schema('movie_list.identifiers',
                                ObjectsContainer.return_identifiers)
Example #34
0
    def get(self, session=None):
        """ Reload Flexget config """
        log.info('Reloading config from disk.')
        try:
            self.manager.load_config()
        except ValueError as e:
            raise ApiError('Error loading config: %s' % e.args[0])

        log.info('Config successfully reloaded from disk.')
        return {}


pid_schema = api.schema('server.pid', {
    'type': 'object',
    'properties': {
        'pid': {
            'type': 'integer'
        }
    }
})


@server_api.route('/pid/')
class ServerPIDAPI(APIResource):
    @api.response(200, description='Reloaded config', model=pid_schema)
    def get(self, session=None):
        """ Get server PID """
        return {'pid': os.getpid()}


shutdown_parser = api.parser()
shutdown_parser.add_argument('force', type=inputs.boolean, required=False, default=False,
Example #35
0
from flexget.api import api, APIResource, ApiClient
from flexget.event import fire_event
from flexget.plugin import PluginError
from flexget.plugins.filter import series

series_api = api.namespace('series', description='Flexget Series operations')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

empty_response = api.schema('empty', {'type': 'object'})

begin_object = {
    'type': 'object',
    'properties': {
        'episode_id': {'type': 'integer'},
        'episode_identifier': {'type': 'string'}
    }
}

release_object = {
    'type': 'object',
    'properties': {
        'release_id': {'type': 'integer'},
Example #36
0
            'rejected_by': {
                'type': 'string'
            }
        },
        'required':
        ['id', 'title', 'url', 'added', 'reason', 'expires', 'rejected_by'],
        'additionalProperties':
        False
    }
    rejected_entries_list_object = {
        'type': 'array',
        'items': rejected_entry_object
    }


rejected_entry_schema = api.schema('rejected_failed_entry_schema',
                                   ObjectsContainer.rejected_entry_object)
rejected_entries_list_schema = api.schema(
    'rejected_entries_list_schema',
    ObjectsContainer.rejected_entries_list_object)

sort_choices = ('added', 'id', 'title', 'url', 'expires', 'rejected_by',
                'reason')
rejected_parser = api.pagination_parser(sort_choices=sort_choices)


@rejected_api.route('/')
class Rejected(APIResource):
    @etag
    @api.response(NotFoundError)
    @api.response(200, model=rejected_entries_list_schema)
    @api.doc(parser=rejected_parser)
Example #37
0
series_api = api.namespace('series', description='Flexget Series operations')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {
            'type': 'string'
        },
        'message': {
            'type': 'string'
        }
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

empty_response = api.schema('empty', {'type': 'object'})

begin_object = {
    'type': 'object',
    'properties': {
        'episode_id': {
            'type': 'integer'
        },
        'episode_identifier': {
            'type': 'string'
        }
    }
}
Example #38
0
from flask import request
from flask_login import current_user

from flexget.api import api, APIResource
from flexget.webserver import change_password, generate_token, WeakPassword

user_api = api.namespace('user', description='Manage user login credentials')

user_password_input = {
    'type': 'object',
    'properties': {
        'password': {'type': 'string'}
    }
}
user_password_input_schema = api.schema('user_password_input', user_password_input)

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

empty_response = api.schema('empty', {'type': 'object'})


@user_api.route('/')
Example #39
0
from flexget.api import api, APIResource
from flexget.plugins.filter import seen

seen_api = api.namespace('seen', description='Managed Flexget seen entries and fields')

PLUGIN_TASK_NAME = 'seen_plugin_API'  # Name of task to use when adding entries via API

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}

empty_response = api.schema('empty', {'type': 'object'})

default_error_schema = api.schema('default_error_schema', default_error_schema)

seen_field_object = {
    'type': 'object',
    'properties': {
        'id': {'type': 'integer'},
        'field': {'type': 'string'},
        'value': {'type': 'string'},
        'added': {'type': 'string', 'format': 'date-time'},
        'seen_entry_id': {'type': 'integer'}
    }
}

seen_object = {
Example #40
0
from builtins import *  # pylint: disable=unused-import, redefined-builtin

import copy

from flask import request, jsonify

from flexget.manager import manager
from flexget.plugins.daemon.scheduler import schedule_schema, scheduler, scheduler_job_map
from flexget.api import api, APIResource

schedule_api = api.namespace('schedules', description='Task Scheduler')

# SwaggerUI does not yet support anyOf or oneOf
schedule_schema = copy.deepcopy(schedule_schema)
schedule_schema['properties']['id'] = {'type': 'integer'}
api_schedule_schema = api.schema('schedules.schedule', schedule_schema)
api_schedules_list_schema = api.schema('schedules.list', {
    'type': 'object',
    'properties': {
        'schedules': {
            'type': 'array',
            'items': schedule_schema
        }
    }
})


def _schedule_by_id(schedule_id):
    for schedule in manager.config.get('schedules', []):
        if id(schedule) == schedule_id:
            schedule = schedule.copy()
Example #41
0
from __future__ import unicode_literals, division, absolute_import
from builtins import *  # pylint: disable=unused-import, redefined-builtin

import logging

from flask import request

from flexget.api import api, APIResource, NotFoundError
from flexget.plugins.modify.config_secrets import secrets_from_db, secrets_to_db

log = logging.getLogger('secrets')

secrets_api = api.namespace('secrets', description='View and edit secrets')
empty_object = api.schema('empty_object', {'type': 'object'})


@secrets_api.route('/')
class SecretsAPI(APIResource):

    @api.response(200)
    @api.response(NotFoundError)
    def get(self, session=None):
        return secrets_from_db()

    @api.response(201, 'Successfully updated secrets file')
    @api.response(NotFoundError)
    @api.validate(empty_object)
    @api.doc(description='Note that editing secrets may not be persistent, depending on user config')
    def put(self, session=None):
        data = request.json
        secrets_to_db(data)
Example #42
0
        'properties': {
            'name': {'type': 'string'},
            'api_ver': {'type': 'integer'},
            'builtin': {'type': 'boolean'},
            'category': {'type': ['string', 'null']},
            'contexts': {'type': 'array', 'items': {'type': 'string'}},
            'debug': {'type': 'boolean'},
            'interfaces': {'type': 'array', 'items': {'type': 'string'}},
            'phase_handlers': {'type': 'array', 'items': phase_object}
        }
    }

    plugin_list_reply = {'type': 'array', 'items': plugin_object}


plugin_schema = api.schema('plugin_object', ObjectsContainer.plugin_object)
plugin_list_reply_schema = api.schema('plugin_list_reply', ObjectsContainer.plugin_list_reply)

plugin_parser = api.parser()
plugin_parser.add_argument('include_schema', type=inputs.boolean, default=False,
                           help='Include plugin schema. This will increase response size')

plugins_parser = api.pagination_parser(plugin_parser)

plugins_parser.add_argument('interface', case_sensitive=False, help='Show plugins which implement this interface')
plugins_parser.add_argument('phase', case_sensitive=False, help='Show plugins that act on this phase')


def plugin_to_dict(plugin):
    return {
        'name': plugin.name,
Example #43
0
        'type': 'object',
        'properties': {
            'id': {'type': 'integer'},
            'name': {'type': 'string'},
            'added_on': {'type': 'string'},
            'title': {'type': 'string'},
            'original_url': {'type': 'string'},
            'entry': base_entry_object,

        }
    }

    entry_lists_entries_return_object = {'type': 'array', 'items': entry_list_entry_base_object}


entry_list_object_schema = api.schema('entry_list_object_schema', ObjectsContainer.entry_list_base_object)
entry_list_input_object_schema = api.schema('entry_list_input_object_schema', ObjectsContainer.entry_list_input_object)
entry_list_return_lists_schema = api.schema('entry_list_return_lists_schema', ObjectsContainer.entry_list_return_lists)

entry_list_parser = api.parser()
entry_list_parser.add_argument('name', help='Filter results by list name')


@entry_list_api.route('/')
class EntryListListsAPI(APIResource):
    @etag
    @api.doc(parser=entry_list_parser)
    @api.response(200, 'Successfully retrieved entry lists', entry_list_return_lists_schema)
    def get(self, session=None):
        """ Get entry lists """
        args = entry_list_parser.parse_args()
Example #44
0
@event('manager.shutdown')
def stop_scheduler(manager):
    if scheduler and scheduler.running:
        scheduler.shutdown(wait=False)


@event('config.register')
def register_config():
    register_config_key('schedules', main_schema)
    register_schema('/schema/config/schedule', schedule_schema)


schedule_api = api.namespace('schedules', description='Task Scheduler')

api_schedule_schema = api.schema('schedule', schedule_schema)
api_schedules_list_schema = api.schema('schedule', main_schema)

def _schedule_by_id(schedule_id):
    for schedule in manager.config.get('schedules', []):
        if id(schedule) == schedule_id:
            schedule = schedule.copy()
            schedule['id'] = schedule_id
            return schedule


@schedule_api.route('/')
class SchedulesAPI(APIResource):

    @api.response(200, 'List of schedules', api_schedules_list_schema)
    def get(self, session=None):
Example #45
0
        'properties': {
            'password': {'type': 'string'}
        },
        'required': ['password'],
        'additionalProperties': False
    }

    user_token_response = {
        'type': 'object',
        'properties': {
            'token': {'type': 'string'}
        }
    }


user_password_input_schema = api.schema('user_password_input', ObjectsContainer.user_password_input)
user_token_response_schema = api.schema('user_token_response', ObjectsContainer.user_token_response)


@user_api.route('/')
@api.doc('Change user password')
class UserManagementAPI(APIResource):
    @api.validate(model=user_password_input_schema, description='Password change schema')
    @api.response(BadRequest)
    @api.response(200, 'Success', model=base_message_schema)
    @api.doc(description='Change user password. A score of at least 3 is needed.'
                         'See https://github.com/dropbox/zxcvbn for details')
    def put(self, session=None):
        """ Change user password """
        user = current_user
        data = request.json
Example #46
0
        'anyOf': [{
            'required': ['begin_episode']
        }, {
            'required': ['alternate_names']
        }],
        'additionalProperties:':
        False
    }

    series_input_object = copy.deepcopy(series_edit_object)
    series_input_object['properties']['name'] = {'type': 'string'}
    del series_input_object['anyOf']
    series_input_object['required'] = ['name']


series_list_schema = api.schema('list_series',
                                ObjectsContainer.series_list_schema)
series_edit_schema = api.schema('series_edit_schema',
                                ObjectsContainer.series_edit_object)
series_input_schema = api.schema('series_input_schema',
                                 ObjectsContainer.series_input_object)
show_details_schema = api.schema('show_details',
                                 ObjectsContainer.single_series_object)

episode_list_schema = api.schema('episode_list',
                                 ObjectsContainer.episode_list_schema)
episode_schema = api.schema('episode_item', ObjectsContainer.episode_object)

release_schema = api.schema('release_schema', ObjectsContainer.release_object)
release_list_schema = api.schema('release_list_schema',
                                 ObjectsContainer.release_list_schema)
Example #47
0
from flask_restplus import inputs

from flexget.api import api, APIResource
from flexget.plugins.internal.api_tvmaze import APITVMaze as tvm

tvmaze_api = api.namespace('tvmaze', description='TVMaze Shows')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

actor_object = {
    'type': 'object',
    'properties': {
        "last_update": {'type': 'string', 'format': 'date-time'},
        "medium_image": {'type': 'string'},
        "name": {'type': 'string'},
        "original_image": {'type': 'string'},
        "tvmaze_id": {'type': 'integer'},
        "url": {'type': 'string'},
    }
}

schedule_object = {
    'type': 'object',
Example #48
0
                'properties': {
                    'details': {'type': 'string'},
                    'filename': {'type': 'string'},
                    'id': {'type': 'integer'},
                    'task': {'type': 'string'},
                    'time': {'type': 'string'},
                    'title': {'type': 'string'},
                    'url': {'type': 'string'}
                }
            }
        },
        'pages': {'type': 'integer'}
    }
}

history_api_schema = api.schema('history.list', history_api_schema)

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}

default_error_schema = api.schema('default_error_schema', default_error_schema)

history_parser = api.parser()
history_parser.add_argument('page', type=int, required=False, default=1, help='Page number')
history_parser.add_argument('max', type=int, required=False, default=50, help='Results per page')
history_parser.add_argument('task', type=str, required=False, default=None, help='Filter by task name')
Example #49
0
            'aliases': {'type': 'array', 'items': {'type': 'string'}},
            'first_aired': {'type': 'string', 'format': 'date-time'},
            'banner': {'type': ['string', 'null']},
            'network': {'type': 'string'},
            'series_name': {'type': 'string'},
            'status': {'type': 'string'},
            'overview': {'type': ['string', 'null']},
            'tvdb_id': {'type': 'integer'}
        },
        'required': ['aliases', 'first_aired', 'banner', 'network', 'series_name', 'status', 'overview', 'tvdb_id'],
        'additionalProperties': False
    }
    search_results_object = {'type': 'array', 'items': search_result_object}


tvdb_series_schema = api.schema('tvdb_series_schema', ObjectsContainer.tvdb_series_object)
tvdb_episode_schema = api.schema('tvdb_episode_schema', ObjectsContainer.episode_object)
search_results_schema = api.schema('tvdb_search_results_schema', ObjectsContainer.search_results_object)

base_parser = api.parser()
base_parser.add_argument('language', default='en', help='Language abbreviation string for different language support')

series_parser = base_parser.copy()
series_parser.add_argument('include_actors', type=inputs.boolean, help='Include actors in response')


@tvdb_api.route('/series/<string:title>/')
@api.doc(params={'title': 'TV Show name or TVDB ID'}, parser=series_parser)
class TVDBSeriesLookupAPI(APIResource):
    @etag
    @api.response(200, 'Successfully found show', tvdb_series_schema)
Example #50
0
from flexget.api import api, APIResource
from flexget.plugins.list import movie_list as ml
from flexget.utils.tools import split_title_year

log = logging.getLogger('movie_list')

movie_list_api = api.namespace('movie_list', description='Movie List operations')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {'type': 'string'},
        'message': {'type': 'string'}
    }
}
empty_response = api.schema('empty', {'type': 'object'})

default_error_schema = api.schema('default_error_schema', default_error_schema)
empty_response = api.schema('empty_response', empty_response)

allowed_ids = ml.SUPPORTED_IDS

input_movie_list_id_object = {
    'type': 'array',
    'items': {
        'type': 'object',
        'minProperties': 1,
        'additionalProperties': True
    }
}
Example #51
0
def check_valid_login():
    # Allow access to root, login and swagger documentation without authentication
    if request.path == '/' or request.path.startswith('/login') or \
            request.path.startswith('/logout') or request.path.startswith('/swagger'):
        return

    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()

# API Authentication and Authorization
login_api = api.namespace('login', description='API Authentication')

login_api_schema = api.schema('login', {
    'type': 'object',
    'properties': {
        'username': {'type': 'string'},
        'password': {'type': 'string'}
    }
})

login_parser = api.parser()
login_parser.add_argument('remember', type=bool, required=False, default=False, help='Remember for next time')


@login_api.route('/')
@api.doc(description='Login to API with username and password')
class LoginAPI(APIResource):

    @api.expect(login_api_schema)
    @api.response(400, 'Invalid username or password')
    @api.response(200, 'Login successful')
Example #52
0
from __future__ import unicode_literals, division, absolute_import
from builtins import *  # pylint: disable=unused-import, redefined-builtin

import logging

from flask import request

from flexget.api import api, APIResource, NotFoundError
from flexget.plugins.modify.config_secrets import secrets_from_db, secrets_to_db

log = logging.getLogger('secrets')

secrets_api = api.namespace('secrets', description='View and edit secrets')
empty_object = api.schema('empty_object', {'type': 'object'})


@secrets_api.route('/')
class SecretsAPI(APIResource):
    @api.response(200)
    @api.response(NotFoundError)
    def get(self, session=None):
        return secrets_from_db()

    @api.response(201, 'Successfully updated secrets file')
    @api.response(NotFoundError)
    @api.validate(empty_object)
    @api.doc(
        description=
        'Note that editing secrets may not be persistent, depending on user config'
    )
    def put(self, session=None):
Example #53
0
entry_list_api = api.namespace('entry_list',
                               description='Entry List operations')

default_error_schema = {
    'type': 'object',
    'properties': {
        'status': {
            'type': 'string'
        },
        'message': {
            'type': 'string'
        }
    }
}
empty_response = api.schema('empty', {'type': 'object'})

default_error_schema = api.schema('default_error_schema', default_error_schema)
entry_list_base_object = {
    'type': 'object',
    'properties': {
        'id': {
            'type': 'integer'
        },
        'name': {
            'type': 'string'
        },
        'added_on': {
            'type': 'string'
        }
    }
Example #54
0
            'name': {'type': 'string'}
        }
    }

    list_input = copy.deepcopy(list_object)
    del list_input['properties']['id']
    del list_input['properties']['added_on']

    return_movies = {'type': 'array', 'items': movie_list_object}

    return_lists = {'type': 'array', 'items': list_object}

    return_identifiers = {'type': 'array', 'items': {'type': 'string'}}


input_movie_entry_schema = api.schema('input_movie_entry', ObjectsContainer.input_movie_entry)
input_movie_list_id_schema = api.schema('input_movie_list_id_object', ObjectsContainer.input_movie_list_id_object)

movie_list_id_object_schema = api.schema('movie_list_id_object', ObjectsContainer.return_movie_list_id_object)
movie_list_object_schema = api.schema('movie_list_object', ObjectsContainer.movie_list_object)
list_object_schema = api.schema('list_object', ObjectsContainer.list_object)
return_lists_schema = api.schema('return_lists', ObjectsContainer.return_lists)
return_movies_schema = api.schema('return_movies', ObjectsContainer.return_movies)

new_list_schema = api.schema('new_list', ObjectsContainer.list_input)
identifiers_schema = api.schema('movie_list.identifiers', ObjectsContainer.return_identifiers)

movie_list_parser = api.parser()
movie_list_parser.add_argument('name', help='Filter results by list name')

Example #55
0
    movie_return_object = copy.deepcopy(base_return_object)
    movie_return_object['properties']['tagline'] = {'type': 'string'}
    movie_return_object['properties']['released'] = {'type': 'string'}
    movie_return_object['properties']['trailer'] = {'type': 'string'}

    default_error_object = {
        'type': 'object',
        'properties': {
            'status': {'type': 'string'},
            'message': {'type': 'string'}
        }
    }


default_error_schema = api.schema('default_error_schema', objects_container.default_error_object)
series_return_schema = api.schema('series_return_schema', objects_container.series_return_object)
movie_return_schema = api.schema('movie_return_schema', objects_container.movie_return_object)

lookup_parser = api.parser()
lookup_parser.add_argument('year', type=int, help='Lookup year')
lookup_parser.add_argument('trakt_id', type=int, help='Trakt ID')
lookup_parser.add_argument('trakt_slug', help='Trakt slug')
lookup_parser.add_argument('tmdb_id', type=int, help='TMDB ID')
lookup_parser.add_argument('imdb_id', help='IMDB ID')
lookup_parser.add_argument('tvdb_id', type=int, help='TVDB ID')
lookup_parser.add_argument('tvrage_id', type=int, help='TVRage ID')
lookup_parser.add_argument('include_actors', type=inputs.boolean, help='Include actors in response')
lookup_parser.add_argument('include_translations', type=inputs.boolean, help='Include translations in response')

Example #56
0
@event('manager.shutdown')
def stop_scheduler(manager):
    if scheduler and scheduler.running:
        scheduler.shutdown(wait=False)


@event('config.register')
def register_config():
    register_config_key('schedules', main_schema)
    register_schema('/schema/config/schedule', schedule_schema)


schedule_api = api.namespace('schedules', description='Task Scheduler')

api_schedule_schema = api.schema('schedule', schedule_schema)
api_schedules_list_schema = api.schema('schedule', main_schema)


def _schedule_by_id(schedule_id):
    for schedule in manager.config.get('schedules', []):
        if id(schedule) == schedule_id:
            schedule = schedule.copy()
            schedule['id'] = schedule_id
            return schedule


@schedule_api.route('/')
class SchedulesAPI(APIResource):
    @api.response(200, 'List of schedules', api_schedules_list_schema)
    def get(self, session=None):