Source code for flexget.components.scheduler.api

import copy

from flask import jsonify, request

from flexget.api import APIResource, api
from flexget.api.app import (
    APIError,
    Conflict,
    NotFoundError,
    base_message_schema,
    etag,
    success_response,
)
from flexget.components.scheduler.scheduler import (
    DEFAULT_SCHEDULES,
    schedule_schema,
    scheduler,
    scheduler_job_map,
)

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


[docs] class ObjectsContainer: # 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_model('schedules.base', schedule_schema) api_schedule_schema = api.schema_model('schedules.schedule', ObjectsContainer.schedule_object) api_schedules_list_schema = api.schema_model('schedules.list', ObjectsContainer.schedules_list)
[docs] 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 ' '(Not displayed as Swagger does yet not support anyOf or oneOf.' )
[docs] @schedule_api.route('/') @api.doc(description=schedule_desc) @api.response(Conflict) class SchedulesAPI(APIResource):
[docs] @etag @api.response(200, model=api_schedules_list_schema) def get(self, session=None): """List schedules.""" schedule_list = [] schedules = self.manager.config.get('schedules', []) # Checks for boolean config if schedules is True: schedules = DEFAULT_SCHEDULES elif schedules is False: raise Conflict('Schedules are disables in config') for schedule in schedules: # Copy the object so we don't apply id to the config schedule_id = id(schedule) schedule = schedule.copy() schedule['id'] = schedule_id schedule_list.append(schedule) return jsonify(schedule_list)
[docs] @api.validate(base_schedule_schema, description='Schedule Object') @api.response(201, model=api_schedule_schema) @api.response(APIError) def post(self, session=None): """Add new schedule.""" data = request.json schedules = self.manager.config.get('schedules', []) # Checks for boolean config if schedules is True: schedules = DEFAULT_SCHEDULES elif schedules is False: raise Conflict('Schedules are disables in config') self.manager.config['schedules'] = schedules self.manager.config['schedules'].append(data) schedules = self.manager.config['schedules'] new_schedule, _ = _schedule_by_id(id(data), schedules) if not new_schedule: raise APIError('schedule went missing after add') self.manager.save_config() self.manager.config_changed() resp = jsonify(new_schedule) resp.status_code = 201 return resp
# noinspection PyUnusedLocal
[docs] @schedule_api.route('/<int:schedule_id>/') @api.doc(params={'schedule_id': 'ID of Schedule'}) @api.doc(description=schedule_desc) @api.response(NotFoundError) @api.response(Conflict) class ScheduleAPI(APIResource):
[docs] @etag @api.response(200, model=api_schedule_schema) def get(self, schedule_id, session=None): """Get schedule details.""" schedules = self.manager.config.get('schedules', []) # Checks for boolean config if schedules is True: schedules = DEFAULT_SCHEDULES elif schedules is False: raise Conflict('Schedules are disables in config') schedule, _ = _schedule_by_id(schedule_id, schedules) if schedule is None: raise NotFoundError(f'schedule {schedule_id} not found') job_id = scheduler_job_map.get(schedule_id) if job_id: job = scheduler.get_job(job_id) if job: schedule['next_run_time'] = job.next_run_time return jsonify(schedule)
[docs] def _update_schedule(self, existing, update, merge=False): if 'id' in update: del update['id'] if not merge: existing.clear() existing.update(update) self.manager.save_config() self.manager.config_changed() return existing
[docs] @api.validate(base_schedule_schema, description='Updated Schedule Object') @api.response(201, model=api_schedule_schema) def put(self, schedule_id, session=None): """Update schedule.""" new_schedule = request.json schedules = self.manager.config.get('schedules', []) # Checks for boolean config if schedules is True: self.manager.config['schedules'] = DEFAULT_SCHEDULES elif schedules is False: raise Conflict('Schedules are disables in config') schedule, idx = _schedule_by_id(schedule_id, self.manager.config['schedules']) if not schedule: raise NotFoundError(f'schedule {schedule_id} not found') new_schedule['id'] = id(schedule) self.manager.config['schedules'][idx] = new_schedule self.manager.save_config() self.manager.config_changed() resp = jsonify(new_schedule) resp.status_code = 201 return resp
[docs] @api.response(200, description='Schedule deleted', model=base_message_schema) def delete(self, schedule_id, session=None): """Delete a schedule.""" schedules = self.manager.config.get('schedules') # Checks for boolean config if schedules is True: raise Conflict('Schedules usage is set to default, cannot delete') if schedules is False: raise Conflict('Schedules are disables in config') for i in range(len(self.manager.config.get('schedules', []))): if id(self.manager.config['schedules'][i]) == schedule_id: del self.manager.config['schedules'][i] self.manager.save_config() self.manager.config_changed() return success_response(f'schedule {schedule_id} successfully deleted') raise NotFoundError(f'schedule {schedule_id} not found')